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