annotatescrollbar.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. ;(function (mod) {
  4. if (typeof exports == 'object' && typeof module == 'object')
  5. // CommonJS
  6. mod(require('../../lib/codemirror'))
  7. else if (typeof define == 'function' && define.amd)
  8. // AMD
  9. define(['../../lib/codemirror'], mod)
  10. // Plain browser env
  11. else mod(CodeMirror)
  12. })(function (CodeMirror) {
  13. 'use strict'
  14. CodeMirror.defineExtension('annotateScrollbar', function (options) {
  15. if (typeof options == 'string') options = { className: options }
  16. return new Annotation(this, options)
  17. })
  18. CodeMirror.defineOption('scrollButtonHeight', 0)
  19. function Annotation(cm, options) {
  20. this.cm = cm
  21. this.options = options
  22. this.buttonHeight = options.scrollButtonHeight || cm.getOption('scrollButtonHeight')
  23. this.annotations = []
  24. this.doRedraw = this.doUpdate = null
  25. this.div = cm.getWrapperElement().appendChild(document.createElement('div'))
  26. this.div.style.cssText = 'position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none'
  27. this.computeScale()
  28. function scheduleRedraw(delay) {
  29. clearTimeout(self.doRedraw)
  30. self.doRedraw = setTimeout(function () {
  31. self.redraw()
  32. }, delay)
  33. }
  34. var self = this
  35. cm.on(
  36. 'refresh',
  37. (this.resizeHandler = function () {
  38. clearTimeout(self.doUpdate)
  39. self.doUpdate = setTimeout(function () {
  40. if (self.computeScale()) scheduleRedraw(20)
  41. }, 100)
  42. })
  43. )
  44. cm.on('markerAdded', this.resizeHandler)
  45. cm.on('markerCleared', this.resizeHandler)
  46. if (options.listenForChanges !== false)
  47. cm.on(
  48. 'changes',
  49. (this.changeHandler = function () {
  50. scheduleRedraw(250)
  51. })
  52. )
  53. }
  54. Annotation.prototype.computeScale = function () {
  55. var cm = this.cm
  56. var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) / cm.getScrollerElement().scrollHeight
  57. if (hScale != this.hScale) {
  58. this.hScale = hScale
  59. return true
  60. }
  61. }
  62. Annotation.prototype.update = function (annotations) {
  63. this.annotations = annotations
  64. this.redraw()
  65. }
  66. Annotation.prototype.redraw = function (compute) {
  67. if (compute !== false) this.computeScale()
  68. var cm = this.cm,
  69. hScale = this.hScale
  70. var frag = document.createDocumentFragment(),
  71. anns = this.annotations
  72. var wrapping = cm.getOption('lineWrapping')
  73. var singleLineH = wrapping && cm.defaultTextHeight() * 1.5
  74. var curLine = null,
  75. curLineObj = null
  76. function getY(pos, top) {
  77. if (curLine != pos.line) {
  78. curLine = pos.line
  79. curLineObj = cm.getLineHandle(pos.line)
  80. var visual = cm.getLineHandleVisualStart(curLineObj)
  81. if (visual != curLineObj) {
  82. curLine = cm.getLineNumber(visual)
  83. curLineObj = visual
  84. }
  85. }
  86. if ((curLineObj.widgets && curLineObj.widgets.length) || (wrapping && curLineObj.height > singleLineH)) return cm.charCoords(pos, 'local')[top ? 'top' : 'bottom']
  87. var topY = cm.heightAtLine(curLineObj, 'local')
  88. return topY + (top ? 0 : curLineObj.height)
  89. }
  90. var lastLine = cm.lastLine()
  91. if (cm.display.barWidth)
  92. for (var i = 0, nextTop; i < anns.length; i++) {
  93. var ann = anns[i]
  94. if (ann.to.line > lastLine) continue
  95. var top = nextTop || getY(ann.from, true) * hScale
  96. var bottom = getY(ann.to, false) * hScale
  97. while (i < anns.length - 1) {
  98. if (anns[i + 1].to.line > lastLine) break
  99. nextTop = getY(anns[i + 1].from, true) * hScale
  100. if (nextTop > bottom + 0.9) break
  101. ann = anns[++i]
  102. bottom = getY(ann.to, false) * hScale
  103. }
  104. if (bottom == top) continue
  105. var height = Math.max(bottom - top, 3)
  106. var elt = frag.appendChild(document.createElement('div'))
  107. elt.style.cssText = 'position: absolute; right: 0px; width: ' + Math.max(cm.display.barWidth - 1, 2) + 'px; top: ' + (top + this.buttonHeight) + 'px; height: ' + height + 'px'
  108. elt.className = this.options.className
  109. if (ann.id) {
  110. elt.setAttribute('annotation-id', ann.id)
  111. }
  112. }
  113. this.div.textContent = ''
  114. this.div.appendChild(frag)
  115. }
  116. Annotation.prototype.clear = function () {
  117. this.cm.off('refresh', this.resizeHandler)
  118. this.cm.off('markerAdded', this.resizeHandler)
  119. this.cm.off('markerCleared', this.resizeHandler)
  120. if (this.changeHandler) this.cm.off('changes', this.changeHandler)
  121. this.div.parentNode.removeChild(this.div)
  122. }
  123. })