Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
scrollspy.js313 linesDownload Raw Back to src
1/**2 * --------------------------------------------------------------------------3 * Bootstrap (v4.6.2): scrollspy.js4 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)5 * --------------------------------------------------------------------------6 */7 8import $ from 'jquery'9import Util from './util'10 11/**12 * Constants13 */14 15const NAME = 'scrollspy'16const VERSION = '4.6.2'17const DATA_KEY = 'bs.scrollspy'18const EVENT_KEY = `.${DATA_KEY}`19const DATA_API_KEY = '.data-api'20const JQUERY_NO_CONFLICT = $.fn[NAME]21 22const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'23const CLASS_NAME_ACTIVE = 'active'24 25const EVENT_ACTIVATE = `activate${EVENT_KEY}`26const EVENT_SCROLL = `scroll${EVENT_KEY}`27const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`28 29const METHOD_OFFSET = 'offset'30const METHOD_POSITION = 'position'31 32const SELECTOR_DATA_SPY = '[data-spy="scroll"]'33const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'34const SELECTOR_NAV_LINKS = '.nav-link'35const SELECTOR_NAV_ITEMS = '.nav-item'36const SELECTOR_LIST_ITEMS = '.list-group-item'37const SELECTOR_DROPDOWN = '.dropdown'38const SELECTOR_DROPDOWN_ITEMS = '.dropdown-item'39const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'40 41const Default = {42  offset: 10,43  method: 'auto',44  target: ''45}46 47const DefaultType = {48  offset: 'number',49  method: 'string',50  target: '(string|element)'51}52 53/**54 * Class definition55 */56 57class ScrollSpy {58  constructor(element, config) {59    this._element = element60    this._scrollElement = element.tagName === 'BODY' ? window : element61    this._config = this._getConfig(config)62    this._selector = `${this._config.target} ${SELECTOR_NAV_LINKS},` +63                          `${this._config.target} ${SELECTOR_LIST_ITEMS},` +64                          `${this._config.target} ${SELECTOR_DROPDOWN_ITEMS}`65    this._offsets = []66    this._targets = []67    this._activeTarget = null68    this._scrollHeight = 069 70    $(this._scrollElement).on(EVENT_SCROLL, event => this._process(event))71 72    this.refresh()73    this._process()74  }75 76  // Getters77  static get VERSION() {78    return VERSION79  }80 81  static get Default() {82    return Default83  }84 85  // Public86  refresh() {87    const autoMethod = this._scrollElement === this._scrollElement.window ?88      METHOD_OFFSET : METHOD_POSITION89 90    const offsetMethod = this._config.method === 'auto' ?91      autoMethod : this._config.method92 93    const offsetBase = offsetMethod === METHOD_POSITION ?94      this._getScrollTop() : 095 96    this._offsets = []97    this._targets = []98 99    this._scrollHeight = this._getScrollHeight()100 101    const targets = [].slice.call(document.querySelectorAll(this._selector))102 103    targets104      .map(element => {105        let target106        const targetSelector = Util.getSelectorFromElement(element)107 108        if (targetSelector) {109          target = document.querySelector(targetSelector)110        }111 112        if (target) {113          const targetBCR = target.getBoundingClientRect()114          if (targetBCR.width || targetBCR.height) {115            // TODO (fat): remove sketch reliance on jQuery position/offset116            return [117              $(target)[offsetMethod]().top + offsetBase,118              targetSelector119            ]120          }121        }122 123        return null124      })125      .filter(Boolean)126      .sort((a, b) => a[0] - b[0])127      .forEach(item => {128        this._offsets.push(item[0])129        this._targets.push(item[1])130      })131  }132 133  dispose() {134    $.removeData(this._element, DATA_KEY)135    $(this._scrollElement).off(EVENT_KEY)136 137    this._element = null138    this._scrollElement = null139    this._config = null140    this._selector = null141    this._offsets = null142    this._targets = null143    this._activeTarget = null144    this._scrollHeight = null145  }146 147  // Private148  _getConfig(config) {149    config = {150      ...Default,151      ...(typeof config === 'object' && config ? config : {})152    }153 154    if (typeof config.target !== 'string' && Util.isElement(config.target)) {155      let id = $(config.target).attr('id')156      if (!id) {157        id = Util.getUID(NAME)158        $(config.target).attr('id', id)159      }160 161      config.target = `#${id}`162    }163 164    Util.typeCheckConfig(NAME, config, DefaultType)165 166    return config167  }168 169  _getScrollTop() {170    return this._scrollElement === window ?171      this._scrollElement.pageYOffset : this._scrollElement.scrollTop172  }173 174  _getScrollHeight() {175    return this._scrollElement.scrollHeight || Math.max(176      document.body.scrollHeight,177      document.documentElement.scrollHeight178    )179  }180 181  _getOffsetHeight() {182    return this._scrollElement === window ?183      window.innerHeight : this._scrollElement.getBoundingClientRect().height184  }185 186  _process() {187    const scrollTop = this._getScrollTop() + this._config.offset188    const scrollHeight = this._getScrollHeight()189    const maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight()190 191    if (this._scrollHeight !== scrollHeight) {192      this.refresh()193    }194 195    if (scrollTop >= maxScroll) {196      const target = this._targets[this._targets.length - 1]197 198      if (this._activeTarget !== target) {199        this._activate(target)200      }201 202      return203    }204 205    if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {206      this._activeTarget = null207      this._clear()208      return209    }210 211    for (let i = this._offsets.length; i--;) {212      const isActiveTarget = this._activeTarget !== this._targets[i] &&213          scrollTop >= this._offsets[i] &&214          (typeof this._offsets[i + 1] === 'undefined' ||215              scrollTop < this._offsets[i + 1])216 217      if (isActiveTarget) {218        this._activate(this._targets[i])219      }220    }221  }222 223  _activate(target) {224    this._activeTarget = target225 226    this._clear()227 228    const queries = this._selector229      .split(',')230      .map(selector => `${selector}[data-target="${target}"],${selector}[href="${target}"]`)231 232    const $link = $([].slice.call(document.querySelectorAll(queries.join(','))))233 234    if ($link.hasClass(CLASS_NAME_DROPDOWN_ITEM)) {235      $link.closest(SELECTOR_DROPDOWN)236        .find(SELECTOR_DROPDOWN_TOGGLE)237        .addClass(CLASS_NAME_ACTIVE)238      $link.addClass(CLASS_NAME_ACTIVE)239    } else {240      // Set triggered link as active241      $link.addClass(CLASS_NAME_ACTIVE)242      // Set triggered links parents as active243      // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor244      $link.parents(SELECTOR_NAV_LIST_GROUP)245        .prev(`${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`)246        .addClass(CLASS_NAME_ACTIVE)247      // Handle special case when .nav-link is inside .nav-item248      $link.parents(SELECTOR_NAV_LIST_GROUP)249        .prev(SELECTOR_NAV_ITEMS)250        .children(SELECTOR_NAV_LINKS)251        .addClass(CLASS_NAME_ACTIVE)252    }253 254    $(this._scrollElement).trigger(EVENT_ACTIVATE, {255      relatedTarget: target256    })257  }258 259  _clear() {260    [].slice.call(document.querySelectorAll(this._selector))261      .filter(node => node.classList.contains(CLASS_NAME_ACTIVE))262      .forEach(node => node.classList.remove(CLASS_NAME_ACTIVE))263  }264 265  // Static266  static _jQueryInterface(config) {267    return this.each(function () {268      let data = $(this).data(DATA_KEY)269      const _config = typeof config === 'object' && config270 271      if (!data) {272        data = new ScrollSpy(this, _config)273        $(this).data(DATA_KEY, data)274      }275 276      if (typeof config === 'string') {277        if (typeof data[config] === 'undefined') {278          throw new TypeError(`No method named "${config}"`)279        }280 281        data[config]()282      }283    })284  }285}286 287/**288 * Data API implementation289 */290 291$(window).on(EVENT_LOAD_DATA_API, () => {292  const scrollSpys = [].slice.call(document.querySelectorAll(SELECTOR_DATA_SPY))293  const scrollSpysLength = scrollSpys.length294 295  for (let i = scrollSpysLength; i--;) {296    const $spy = $(scrollSpys[i])297    ScrollSpy._jQueryInterface.call($spy, $spy.data())298  }299})300 301/**302 * jQuery303 */304 305$.fn[NAME] = ScrollSpy._jQueryInterface306$.fn[NAME].Constructor = ScrollSpy307$.fn[NAME].noConflict = () => {308  $.fn[NAME] = JQUERY_NO_CONFLICT309  return ScrollSpy._jQueryInterface310}311 312export default ScrollSpy313 
codekingpro/portable-devtools · Team Ai