openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

scrollbars.min.js (7417B)


      1 import { addClass, elt, rmClass } from "../util/dom.js"
      2 import { on } from "../util/event.js"
      3 import { scrollGap, paddingVert } from "../measurement/position_measurement.js"
      4 import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser.js"
      5 import { updateHeightsInViewport } from "./update_lines.js"
      6 import { Delayed } from "../util/misc.js"
      7 
      8 import { setScrollLeft, updateScrollTop } from "./scrolling.js"
      9 
     10 // SCROLLBARS
     11 
     12 // Prepare DOM reads needed to update the scrollbars. Done in one
     13 // shot to minimize update/measure roundtrips.
     14 export function measureForScrollbars(cm) {
     15   let d = cm.display, gutterW = d.gutters.offsetWidth
     16   let docH = Math.round(cm.doc.height + paddingVert(cm.display))
     17   return {
     18     clientHeight: d.scroller.clientHeight,
     19     viewHeight: d.wrapper.clientHeight,
     20     scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
     21     viewWidth: d.wrapper.clientWidth,
     22     barLeft: cm.options.fixedGutter ? gutterW : 0,
     23     docHeight: docH,
     24     scrollHeight: docH + scrollGap(cm) + d.barHeight,
     25     nativeBarWidth: d.nativeBarWidth,
     26     gutterWidth: gutterW
     27   }
     28 }
     29 
     30 class NativeScrollbars {
     31   constructor(place, scroll, cm) {
     32     this.cm = cm
     33     let vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
     34     let horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
     35     place(vert); place(horiz)
     36 
     37     on(vert, "scroll", () => {
     38       if (vert.clientHeight) scroll(vert.scrollTop, "vertical")
     39     })
     40     on(horiz, "scroll", () => {
     41       if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal")
     42     })
     43 
     44     this.checkedZeroWidth = false
     45     // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
     46     if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px"
     47   }
     48 
     49   update(measure) {
     50     let needsH = measure.scrollWidth > measure.clientWidth + 1
     51     let needsV = measure.scrollHeight > measure.clientHeight + 1
     52     let sWidth = measure.nativeBarWidth
     53 
     54     if (needsV) {
     55       this.vert.style.display = "block"
     56       this.vert.style.bottom = needsH ? sWidth + "px" : "0"
     57       let totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
     58       // A bug in IE8 can cause this value to be negative, so guard it.
     59       this.vert.firstChild.style.height =
     60         Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
     61     } else {
     62       this.vert.style.display = ""
     63       this.vert.firstChild.style.height = "0"
     64     }
     65 
     66     if (needsH) {
     67       this.horiz.style.display = "block"
     68       this.horiz.style.right = needsV ? sWidth + "px" : "0"
     69       this.horiz.style.left = measure.barLeft + "px"
     70       let totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
     71       this.horiz.firstChild.style.width =
     72         Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
     73     } else {
     74       this.horiz.style.display = ""
     75       this.horiz.firstChild.style.width = "0"
     76     }
     77 
     78     if (!this.checkedZeroWidth && measure.clientHeight > 0) {
     79       if (sWidth == 0) this.zeroWidthHack()
     80       this.checkedZeroWidth = true
     81     }
     82 
     83     return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
     84   }
     85 
     86   setScrollLeft(pos) {
     87     if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos
     88     if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz")
     89   }
     90 
     91   setScrollTop(pos) {
     92     if (this.vert.scrollTop != pos) this.vert.scrollTop = pos
     93     if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert, "vert")
     94   }
     95 
     96   zeroWidthHack() {
     97     let w = mac && !mac_geMountainLion ? "12px" : "18px"
     98     this.horiz.style.height = this.vert.style.width = w
     99     this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
    100     this.disableHoriz = new Delayed
    101     this.disableVert = new Delayed
    102   }
    103 
    104   enableZeroWidthBar(bar, delay, type) {
    105     bar.style.pointerEvents = "auto"
    106     function maybeDisable() {
    107       // To find out whether the scrollbar is still visible, we
    108       // check whether the element under the pixel in the bottom
    109       // right corner of the scrollbar box is the scrollbar box
    110       // itself (when the bar is still visible) or its filler child
    111       // (when the bar is hidden). If it is still visible, we keep
    112       // it enabled, if it's hidden, we disable pointer events.
    113       let box = bar.getBoundingClientRect()
    114       let elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
    115           : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1)
    116       if (elt != bar) bar.style.pointerEvents = "none"
    117       else delay.set(1000, maybeDisable)
    118     }
    119     delay.set(1000, maybeDisable)
    120   }
    121 
    122   clear() {
    123     let parent = this.horiz.parentNode
    124     parent.removeChild(this.horiz)
    125     parent.removeChild(this.vert)
    126   }
    127 }
    128 
    129 class NullScrollbars {
    130   update() { return {bottom: 0, right: 0} }
    131   setScrollLeft() {}
    132   setScrollTop() {}
    133   clear() {}
    134 }
    135 
    136 export function updateScrollbars(cm, measure) {
    137   if (!measure) measure = measureForScrollbars(cm)
    138   let startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
    139   updateScrollbarsInner(cm, measure)
    140   for (let i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
    141     if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
    142       updateHeightsInViewport(cm)
    143     updateScrollbarsInner(cm, measureForScrollbars(cm))
    144     startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
    145   }
    146 }
    147 
    148 // Re-synchronize the fake scrollbars with the actual size of the
    149 // content.
    150 function updateScrollbarsInner(cm, measure) {
    151   let d = cm.display
    152   let sizes = d.scrollbars.update(measure)
    153 
    154   d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
    155   d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
    156   d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
    157 
    158   if (sizes.right && sizes.bottom) {
    159     d.scrollbarFiller.style.display = "block"
    160     d.scrollbarFiller.style.height = sizes.bottom + "px"
    161     d.scrollbarFiller.style.width = sizes.right + "px"
    162   } else d.scrollbarFiller.style.display = ""
    163   if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
    164     d.gutterFiller.style.display = "block"
    165     d.gutterFiller.style.height = sizes.bottom + "px"
    166     d.gutterFiller.style.width = measure.gutterWidth + "px"
    167   } else d.gutterFiller.style.display = ""
    168 }
    169 
    170 export let scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
    171 
    172 export function initScrollbars(cm) {
    173   if (cm.display.scrollbars) {
    174     cm.display.scrollbars.clear()
    175     if (cm.display.scrollbars.addClass)
    176       rmClass(cm.display.wrapper, cm.display.scrollbars.addClass)
    177   }
    178 
    179   cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](node => {
    180     cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
    181     // Prevent clicks in the scrollbars from killing focus
    182     on(node, "mousedown", () => {
    183       if (cm.state.focused) setTimeout(() => cm.display.input.focus(), 0)
    184     })
    185     node.setAttribute("cm-not-content", "true")
    186   }, (pos, axis) => {
    187     if (axis == "horizontal") setScrollLeft(cm, pos)
    188     else updateScrollTop(cm, pos)
    189   }, cm)
    190   if (cm.display.scrollbars.addClass)
    191     addClass(cm.display.wrapper, cm.display.scrollbars.addClass)
    192 }