{"version":3,"sources":["jsdelivr-header.js","/npm/hexo-theme-butterfly@5.5.3/source/js/search/local-search.js"],"names":[],"mappings":"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","file":"/npm/hexo-theme-butterfly@5.5.3/source/js/search/local-search.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/hexo-theme-butterfly@5.5.3/source/js/search/local-search.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/**\r\n * Refer to hexo-generator-searchdb\r\n * https://github.com/next-theme/hexo-generator-searchdb/blob/main/dist/search.js\r\n * Modified by hexo-theme-butterfly\r\n */\r\n\r\nclass LocalSearch {\r\n  constructor ({\r\n    path = '',\r\n    unescape = false,\r\n    top_n_per_article = 1\r\n  }) {\r\n    this.path = path\r\n    this.unescape = unescape\r\n    this.top_n_per_article = top_n_per_article\r\n    this.isfetched = false\r\n    this.datas = null\r\n  }\r\n\r\n  getIndexByWord (words, text, caseSensitive = false) {\r\n    const index = []\r\n    const included = new Set()\r\n\r\n    if (!caseSensitive) {\r\n      text = text.toLowerCase()\r\n    }\r\n    words.forEach(word => {\r\n      if (this.unescape) {\r\n        const div = document.createElement('div')\r\n        div.innerText = word\r\n        word = div.innerHTML\r\n      }\r\n      const wordLen = word.length\r\n      if (wordLen === 0) return\r\n      let startPosition = 0\r\n      let position = -1\r\n      if (!caseSensitive) {\r\n        word = word.toLowerCase()\r\n      }\r\n      while ((position = text.indexOf(word, startPosition)) > -1) {\r\n        index.push({ position, word })\r\n        included.add(word)\r\n        startPosition = position + wordLen\r\n      }\r\n    })\r\n    // Sort index by position of keyword\r\n    index.sort((left, right) => {\r\n      if (left.position !== right.position) {\r\n        return left.position - right.position\r\n      }\r\n      return right.word.length - left.word.length\r\n    })\r\n    return [index, included]\r\n  }\r\n\r\n  // Merge hits into slices\r\n  mergeIntoSlice (start, end, index) {\r\n    let item = index[0]\r\n    let { position, word } = item\r\n    const hits = []\r\n    const count = new Set()\r\n    while (position + word.length <= end && index.length !== 0) {\r\n      count.add(word)\r\n      hits.push({\r\n        position,\r\n        length: word.length\r\n      })\r\n      const wordEnd = position + word.length\r\n\r\n      // Move to next position of hit\r\n      index.shift()\r\n      while (index.length !== 0) {\r\n        item = index[0]\r\n        position = item.position\r\n        word = item.word\r\n        if (wordEnd > position) {\r\n          index.shift()\r\n        } else {\r\n          break\r\n        }\r\n      }\r\n    }\r\n    return {\r\n      hits,\r\n      start,\r\n      end,\r\n      count: count.size\r\n    }\r\n  }\r\n\r\n  // Highlight title and content\r\n  highlightKeyword (val, slice) {\r\n    let result = ''\r\n    let index = slice.start\r\n    for (const { position, length } of slice.hits) {\r\n      result += val.substring(index, position)\r\n      index = position + length\r\n      result += `<mark class=\"search-keyword\">${val.substr(position, length)}</mark>`\r\n    }\r\n    result += val.substring(index, slice.end)\r\n    return result\r\n  }\r\n\r\n  getResultItems (keywords) {\r\n    const resultItems = []\r\n    this.datas.forEach(({ title, content, url }) => {\r\n      // The number of different keywords included in the article.\r\n      const [indexOfTitle, keysOfTitle] = this.getIndexByWord(keywords, title)\r\n      const [indexOfContent, keysOfContent] = this.getIndexByWord(keywords, content)\r\n      const includedCount = new Set([...keysOfTitle, ...keysOfContent]).size\r\n\r\n      // Show search results\r\n      const hitCount = indexOfTitle.length + indexOfContent.length\r\n      if (hitCount === 0) return\r\n\r\n      const slicesOfTitle = []\r\n      if (indexOfTitle.length !== 0) {\r\n        slicesOfTitle.push(this.mergeIntoSlice(0, title.length, indexOfTitle))\r\n      }\r\n\r\n      let slicesOfContent = []\r\n      while (indexOfContent.length !== 0) {\r\n        const item = indexOfContent[0]\r\n        const { position } = item\r\n        // Cut out 120 characters. The maxlength of .search-input is 80.\r\n        const start = Math.max(0, position - 20)\r\n        const end = Math.min(content.length, position + 100)\r\n        slicesOfContent.push(this.mergeIntoSlice(start, end, indexOfContent))\r\n      }\r\n\r\n      // Sort slices in content by included keywords' count and hits' count\r\n      slicesOfContent.sort((left, right) => {\r\n        if (left.count !== right.count) {\r\n          return right.count - left.count\r\n        } else if (left.hits.length !== right.hits.length) {\r\n          return right.hits.length - left.hits.length\r\n        }\r\n        return left.start - right.start\r\n      })\r\n\r\n      // Select top N slices in content\r\n      const upperBound = parseInt(this.top_n_per_article, 10)\r\n      if (upperBound >= 0) {\r\n        slicesOfContent = slicesOfContent.slice(0, upperBound)\r\n      }\r\n\r\n      let resultItem = ''\r\n\r\n      url = new URL(url, location.origin)\r\n      url.searchParams.append('highlight', keywords.join(' '))\r\n\r\n      if (slicesOfTitle.length !== 0) {\r\n        resultItem += `<li class=\"local-search-hit-item\"><a href=\"${url.href}\"><span class=\"search-result-title\">${this.highlightKeyword(title, slicesOfTitle[0])}</span>`\r\n      } else {\r\n        resultItem += `<li class=\"local-search-hit-item\"><a href=\"${url.href}\"><span class=\"search-result-title\">${title}</span>`\r\n      }\r\n\r\n      slicesOfContent.forEach(slice => {\r\n        resultItem += `<p class=\"search-result\">${this.highlightKeyword(content, slice)}...</p>`\r\n      })\r\n\r\n      resultItem += '</a></li>'\r\n      resultItems.push({\r\n        item: resultItem,\r\n        id: resultItems.length,\r\n        hitCount,\r\n        includedCount\r\n      })\r\n    })\r\n    return resultItems\r\n  }\r\n\r\n  fetchData () {\r\n    const isXml = !this.path.endsWith('json')\r\n    fetch(this.path)\r\n      .then(response => response.text())\r\n      .then(res => {\r\n        // Get the contents from search data\r\n        this.isfetched = true\r\n        this.datas = isXml\r\n          ? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => ({\r\n              title: element.querySelector('title').textContent,\r\n              content: element.querySelector('content').textContent,\r\n              url: element.querySelector('url').textContent\r\n            }))\r\n          : JSON.parse(res)\r\n        // Only match articles with non-empty titles\r\n        this.datas = this.datas.filter(data => data.title).map(data => {\r\n          data.title = data.title.trim()\r\n          data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : ''\r\n          data.url = decodeURIComponent(data.url).replace(/\\/{2,}/g, '/')\r\n          return data\r\n        })\r\n        // Remove loading animation\r\n        window.dispatchEvent(new Event('search:loaded'))\r\n      })\r\n  }\r\n\r\n  // Highlight by wrapping node in mark elements with the given class name\r\n  highlightText (node, slice, className) {\r\n    const val = node.nodeValue\r\n    let index = slice.start\r\n    const children = []\r\n    for (const { position, length } of slice.hits) {\r\n      const text = document.createTextNode(val.substring(index, position))\r\n      index = position + length\r\n      const mark = document.createElement('mark')\r\n      mark.className = className\r\n      mark.appendChild(document.createTextNode(val.substr(position, length)))\r\n      children.push(text, mark)\r\n    }\r\n    node.nodeValue = val.substring(index, slice.end)\r\n    children.forEach(element => {\r\n      node.parentNode.insertBefore(element, node)\r\n    })\r\n  }\r\n\r\n  // Highlight the search words provided in the url in the text\r\n  highlightSearchWords (body) {\r\n    const params = new URL(location.href).searchParams.get('highlight')\r\n    const keywords = params ? params.split(' ') : []\r\n    if (!keywords.length || !body) return\r\n    const walk = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null)\r\n    const allNodes = []\r\n    while (walk.nextNode()) {\r\n      if (!walk.currentNode.parentNode.matches('button, select, textarea, .mermaid')) allNodes.push(walk.currentNode)\r\n    }\r\n    allNodes.forEach(node => {\r\n      const [indexOfNode] = this.getIndexByWord(keywords, node.nodeValue)\r\n      if (!indexOfNode.length) return\r\n      const slice = this.mergeIntoSlice(0, node.nodeValue.length, indexOfNode)\r\n      this.highlightText(node, slice, 'search-keyword')\r\n    })\r\n  }\r\n}\r\n\r\nwindow.addEventListener('load', () => {\r\n// Search\r\n  const { path, top_n_per_article, unescape, languages, pagination } = GLOBAL_CONFIG.localSearch\r\n  const enablePagination = pagination && pagination.enable\r\n  const localSearch = new LocalSearch({\r\n    path,\r\n    top_n_per_article,\r\n    unescape\r\n  })\r\n\r\n  const input = document.querySelector('.local-search-input input')\r\n  const statsItem = document.getElementById('local-search-stats')\r\n  const $loadingStatus = document.getElementById('loading-status')\r\n  const isXml = !path.endsWith('json')\r\n\r\n  // Pagination variables (only initialize if pagination is enabled)\r\n  let currentPage = 0\r\n  const hitsPerPage = pagination.hitsPerPage || 10\r\n\r\n  let currentResultItems = []\r\n\r\n  if (!enablePagination) {\r\n    // If pagination is disabled, we don't need these variables\r\n    currentPage = undefined\r\n    currentResultItems = undefined\r\n  }\r\n\r\n  // Cache frequently used elements\r\n  const elements = {\r\n    get pagination () { return document.getElementById('local-search-pagination') },\r\n    get paginationList () { return document.querySelector('#local-search-pagination .ais-Pagination-list') }\r\n  }\r\n\r\n  // Show/hide search results area\r\n  const toggleResultsVisibility = hasResults => {\r\n    if (enablePagination) {\r\n      elements.pagination.style.display = hasResults ? '' : 'none'\r\n    } else {\r\n      elements.pagination.style.display = 'none'\r\n    }\r\n  }\r\n\r\n  // Render search results for current page\r\n  const renderResults = (searchText, resultItems) => {\r\n    const container = document.getElementById('local-search-results')\r\n\r\n    // Determine items to display based on pagination mode\r\n    const itemsToDisplay = enablePagination\r\n      ? currentResultItems.slice(currentPage * hitsPerPage, (currentPage + 1) * hitsPerPage)\r\n      : resultItems\r\n\r\n    // Handle empty page in pagination mode\r\n    if (enablePagination && itemsToDisplay.length === 0 && currentResultItems.length > 0) {\r\n      currentPage = 0\r\n      renderResults(searchText, resultItems)\r\n      return\r\n    }\r\n\r\n    // Add numbering to items\r\n    const numberedItems = itemsToDisplay.map((result, index) => {\r\n      const itemNumber = enablePagination\r\n        ? currentPage * hitsPerPage + index + 1\r\n        : index + 1\r\n      return result.item.replace(\r\n        '<li class=\"local-search-hit-item\">',\r\n        `<li class=\"local-search-hit-item\" value=\"${itemNumber}\">`\r\n      )\r\n    })\r\n\r\n    container.innerHTML = `<ol class=\"search-result-list\">${numberedItems.join('')}</ol>`\r\n\r\n    // Update stats\r\n    const displayCount = enablePagination ? currentResultItems.length : resultItems.length\r\n    const stats = languages.hits_stats.replace(/\\$\\{hits}/, displayCount)\r\n    statsItem.innerHTML = `<hr><div class=\"search-result-stats\">${stats}</div>`\r\n\r\n    // Handle pagination\r\n    if (enablePagination) {\r\n      const nbPages = Math.ceil(currentResultItems.length / hitsPerPage)\r\n      renderPagination(currentPage, nbPages, searchText)\r\n    }\r\n\r\n    const hasResults = resultItems.length > 0\r\n    toggleResultsVisibility(hasResults)\r\n\r\n    window.pjax && window.pjax.refresh(container)\r\n  }\r\n\r\n  // Render pagination\r\n  const renderPagination = (page, nbPages, query) => {\r\n    if (nbPages <= 1) {\r\n      elements.pagination.style.display = 'none'\r\n      elements.paginationList.innerHTML = ''\r\n      return\r\n    }\r\n\r\n    elements.pagination.style.display = 'block'\r\n\r\n    const isFirstPage = page === 0\r\n    const isLastPage = page === nbPages - 1\r\n\r\n    // Responsive page display\r\n    const isMobile = window.innerWidth < 768\r\n    const maxVisiblePages = isMobile ? 3 : 5\r\n    let startPage = Math.max(0, page - Math.floor(maxVisiblePages / 2))\r\n    const endPage = Math.min(nbPages - 1, startPage + maxVisiblePages - 1)\r\n\r\n    // Adjust starting page to maintain max visible pages\r\n    if (endPage - startPage + 1 < maxVisiblePages) {\r\n      startPage = Math.max(0, endPage - maxVisiblePages + 1)\r\n    }\r\n\r\n    let pagesHTML = ''\r\n\r\n    // Only add ellipsis and first page when there are many pages\r\n    if (nbPages > maxVisiblePages && startPage > 0) {\r\n      pagesHTML += `\r\n        <li class=\"ais-Pagination-item ais-Pagination-item--page\">\r\n          <a class=\"ais-Pagination-link\" aria-label=\"Page 1\" href=\"#\" data-page=\"0\">1</a>\r\n        </li>`\r\n      if (startPage > 1) {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--ellipsis\">\r\n            <span class=\"ais-Pagination-link\">...</span>\r\n          </li>`\r\n      }\r\n    }\r\n\r\n    // Add middle page numbers\r\n    for (let i = startPage; i <= endPage; i++) {\r\n      const isSelected = i === page\r\n      if (isSelected) {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--page ais-Pagination-item--selected\">\r\n            <span class=\"ais-Pagination-link\" aria-label=\"Page ${i + 1}\">${i + 1}</span>\r\n          </li>`\r\n      } else {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--page\">\r\n            <a class=\"ais-Pagination-link\" aria-label=\"Page ${i + 1}\" href=\"#\" data-page=\"${i}\">${i + 1}</a>\r\n          </li>`\r\n      }\r\n    }\r\n\r\n    // Only add ellipsis and last page when there are many pages\r\n    if (nbPages > maxVisiblePages && endPage < nbPages - 1) {\r\n      if (endPage < nbPages - 2) {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--ellipsis\">\r\n            <span class=\"ais-Pagination-link\">...</span>\r\n          </li>`\r\n      }\r\n      pagesHTML += `\r\n        <li class=\"ais-Pagination-item ais-Pagination-item--page\">\r\n          <a class=\"ais-Pagination-link\" aria-label=\"Page ${nbPages}\" href=\"#\" data-page=\"${nbPages - 1}\">${nbPages}</a>\r\n        </li>`\r\n    }\r\n\r\n    if (nbPages > 1) {\r\n      elements.paginationList.innerHTML = `\r\n            <li class=\"ais-Pagination-item ais-Pagination-item--previousPage ${isFirstPage ? 'ais-Pagination-item--disabled' : ''}\">\r\n              ${isFirstPage\r\n                ? '<span class=\"ais-Pagination-link ais-Pagination-link--disabled\" aria-label=\"Previous Page\"><i class=\"fas fa-angle-left\"></i></span>'\r\n                : `<a class=\"ais-Pagination-link\" aria-label=\"Previous Page\" href=\"#\" data-page=\"${page - 1}\"><i class=\"fas fa-angle-left\"></i></a>`\r\n              }\r\n            </li>\r\n            ${pagesHTML}\r\n            <li class=\"ais-Pagination-item ais-Pagination-item--nextPage ${isLastPage ? 'ais-Pagination-item--disabled' : ''}\">\r\n              ${isLastPage\r\n                ? '<span class=\"ais-Pagination-link ais-Pagination-link--disabled\" aria-label=\"Next Page\"><i class=\"fas fa-angle-right\"></i></span>'\r\n                : `<a class=\"ais-Pagination-link\" aria-label=\"Next Page\" href=\"#\" data-page=\"${page + 1}\"><i class=\"fas fa-angle-right\"></i></a>`\r\n              }\r\n            </li>`\r\n    } else {\r\n      elements.pagination.style.display = 'none'\r\n    }\r\n  }\r\n\r\n  // Clear search results and stats\r\n  const clearSearchResults = () => {\r\n    const container = document.getElementById('local-search-results')\r\n    container.textContent = ''\r\n    statsItem.textContent = ''\r\n    toggleResultsVisibility(false)\r\n    if (enablePagination) {\r\n      currentResultItems = []\r\n      currentPage = 0\r\n    }\r\n  }\r\n\r\n  // Show no results message\r\n  const showNoResults = searchText => {\r\n    const container = document.getElementById('local-search-results')\r\n    container.textContent = ''\r\n    const statsDiv = document.createElement('div')\r\n    statsDiv.className = 'search-result-stats'\r\n    statsDiv.textContent = languages.hits_empty.replace(/\\$\\{query}/, searchText)\r\n    statsItem.innerHTML = statsDiv.outerHTML\r\n    toggleResultsVisibility(false)\r\n    if (enablePagination) {\r\n      currentResultItems = []\r\n      currentPage = 0\r\n    }\r\n  }\r\n\r\n  const inputEventFunction = () => {\r\n    if (!localSearch.isfetched) return\r\n    let searchText = input.value.trim().toLowerCase()\r\n    isXml && (searchText = searchText.replace(/</g, '&lt;').replace(/>/g, '&gt;'))\r\n\r\n    if (searchText !== '') $loadingStatus.hidden = false\r\n\r\n    const keywords = searchText.split(/[-\\s]+/)\r\n    let resultItems = []\r\n\r\n    if (searchText.length > 0) {\r\n      resultItems = localSearch.getResultItems(keywords)\r\n    }\r\n\r\n    if (keywords.length === 1 && keywords[0] === '') {\r\n      clearSearchResults()\r\n    } else if (resultItems.length === 0) {\r\n      showNoResults(searchText)\r\n    } else {\r\n      // Sort results by relevance\r\n      resultItems.sort((left, right) => {\r\n        if (left.includedCount !== right.includedCount) {\r\n          return right.includedCount - left.includedCount\r\n        } else if (left.hitCount !== right.hitCount) {\r\n          return right.hitCount - left.hitCount\r\n        }\r\n        return right.id - left.id\r\n      })\r\n\r\n      if (enablePagination) {\r\n        currentResultItems = resultItems\r\n        currentPage = 0\r\n      }\r\n      renderResults(searchText, resultItems)\r\n    }\r\n\r\n    $loadingStatus.hidden = true\r\n  }\r\n\r\n  let loadFlag = false\r\n  const $searchMask = document.getElementById('search-mask')\r\n  const $searchDialog = document.querySelector('#local-search .search-dialog')\r\n\r\n  // fix safari\r\n  const fixSafariHeight = () => {\r\n    if (window.innerWidth < 768) {\r\n      $searchDialog.style.setProperty('--search-height', window.innerHeight + 'px')\r\n    }\r\n  }\r\n\r\n  const openSearch = () => {\r\n    btf.overflowPaddingR.add()\r\n    btf.animateIn($searchMask, 'to_show 0.5s')\r\n    btf.animateIn($searchDialog, 'titleScale 0.5s')\r\n    setTimeout(() => { input.focus() }, 300)\r\n    if (!loadFlag) {\r\n      !localSearch.isfetched && localSearch.fetchData()\r\n      input.addEventListener('input', inputEventFunction)\r\n      loadFlag = true\r\n    }\r\n    // shortcut: ESC\r\n    document.addEventListener('keydown', function f (event) {\r\n      if (event.code === 'Escape') {\r\n        closeSearch()\r\n        document.removeEventListener('keydown', f)\r\n      }\r\n    })\r\n\r\n    fixSafariHeight()\r\n    window.addEventListener('resize', fixSafariHeight)\r\n  }\r\n\r\n  const closeSearch = () => {\r\n    btf.overflowPaddingR.remove()\r\n    btf.animateOut($searchDialog, 'search_close .5s')\r\n    btf.animateOut($searchMask, 'to_hide 0.5s')\r\n    window.removeEventListener('resize', fixSafariHeight)\r\n  }\r\n\r\n  const searchClickFn = () => {\r\n    btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', openSearch)\r\n  }\r\n\r\n  const searchFnOnce = () => {\r\n    document.querySelector('#local-search .search-close-button').addEventListener('click', closeSearch)\r\n    $searchMask.addEventListener('click', closeSearch)\r\n    if (GLOBAL_CONFIG.localSearch.preload) {\r\n      localSearch.fetchData()\r\n    }\r\n    localSearch.highlightSearchWords(document.getElementById('article-container'))\r\n\r\n    // Pagination event delegation - only add if pagination is enabled\r\n    if (enablePagination) {\r\n      elements.pagination.addEventListener('click', e => {\r\n        e.preventDefault()\r\n        const link = e.target.closest('a[data-page]')\r\n        if (link) {\r\n          const page = parseInt(link.dataset.page, 10)\r\n          if (!isNaN(page) && currentResultItems.length > 0) {\r\n            currentPage = page\r\n            renderResults(input.value.trim().toLowerCase(), currentResultItems)\r\n          }\r\n        }\r\n      })\r\n    }\r\n\r\n    // Initial state\r\n    toggleResultsVisibility(false)\r\n  }\r\n\r\n  window.addEventListener('search:loaded', () => {\r\n    const $loadDataItem = document.getElementById('loading-database')\r\n    $loadDataItem.nextElementSibling.style.visibility = 'visible'\r\n    $loadDataItem.remove()\r\n  })\r\n\r\n  searchClickFn()\r\n  searchFnOnce()\r\n\r\n  // pjax\r\n  window.addEventListener('pjax:complete', () => {\r\n    !btf.isHidden($searchMask) && closeSearch()\r\n    localSearch.highlightSearchWords(document.getElementById('article-container'))\r\n    searchClickFn()\r\n  })\r\n})\r\n"]}