| @@ 1-148 (lines=148) @@ | ||
| 1 | Mivhak.component('vertical-scrollbar', { |
|
| 2 | template: '<div class="mivhak-scrollbar mivhak-v-scrollbar"><div class="mivhak-scrollbar-thumb"></div></div>', |
|
| 3 | props: { |
|
| 4 | editor: null, |
|
| 5 | $inner: null, |
|
| 6 | $outer: null, |
|
| 7 | mivhakInstance: null, |
|
| 8 | minHeight: 50, |
|
| 9 | state: { |
|
| 10 | a: 0, // The total height of the editor |
|
| 11 | b: 0, // The height of the viewport, excluding padding |
|
| 12 | c: 0, // The height of the viewport, including padding |
|
| 13 | d: 0, // The calculated thumb height |
|
| 14 | t: 0 // The current top offset of the viewport |
|
| 15 | }, |
|
| 16 | initialized: false |
|
| 17 | }, |
|
| 18 | methods: { |
|
| 19 | initialize: function() { |
|
| 20 | if(!this.initialized) |
|
| 21 | { |
|
| 22 | this.initialized = true; |
|
| 23 | this.dragDealer(); |
|
| 24 | var $this = this; |
|
| 25 | this.$inner.on('mousewheel', function(e){$this.onScroll.call(this, e);}); |
|
| 26 | $(window).resize(function(){ |
|
| 27 | if($this.mivhakInstance.state.lineWrap) |
|
| 28 | $this.refresh(); |
|
| 29 | }); |
|
| 30 | } |
|
| 31 | // Refresh anytime initialize is called |
|
| 32 | this.refresh(); |
|
| 33 | }, |
|
| 34 | updateState: function() { |
|
| 35 | var oldState = $.extend({}, this.state); |
|
| 36 | this.state.a = getEditorHeight(this.$inner); |
|
| 37 | this.state.b = this.mivhakInstance.state.height; |
|
| 38 | this.state.c = this.mivhakInstance.state.height-this.mivhakInstance.options.padding*2; |
|
| 39 | this.state.d = Math.max(this.state.c*this.state.b/this.state.a,this.minHeight); |
|
| 40 | this.state.t *= this.state.a/Math.max(oldState.a,1); // Math.max used to prevent division by zero |
|
| 41 | return this.state.a !== oldState.a || this.state.b !== oldState.b; |
|
| 42 | }, |
|
| 43 | refresh: function() { |
|
| 44 | var $this = this, oldTop = this.state.t; |
|
| 45 | raf(function(){ |
|
| 46 | if($this.updateState()) |
|
| 47 | { |
|
| 48 | if($this.getDifference() > 0) |
|
| 49 | { |
|
| 50 | $this.doScroll('up',oldTop-$this.state.t); |
|
| 51 | $this.$el.css({height: $this.state.d + 'px', top: 0}); |
|
| 52 | $this.moveBar(); |
|
| 53 | } |
|
| 54 | else |
|
| 55 | { |
|
| 56 | $this.doScroll('up',$this.state.t); |
|
| 57 | $this.$el.css({height: 0}); |
|
| 58 | } |
|
| 59 | } |
|
| 60 | }); |
|
| 61 | }, |
|
| 62 | onScroll: function(e) { |
|
| 63 | var didScroll; |
|
| 64 | ||
| 65 | if(e.deltaY > 0) |
|
| 66 | didScroll = this.doScroll('up',e.deltaY*e.deltaFactor); |
|
| 67 | else |
|
| 68 | didScroll = this.doScroll('down',-e.deltaY*e.deltaFactor); |
|
| 69 | ||
| 70 | if(0 !== didScroll) { |
|
| 71 | this.moveBar(); |
|
| 72 | e.preventDefault(); // Only prevent page scroll if the editor can be scrolled |
|
| 73 | } |
|
| 74 | }, |
|
| 75 | dragDealer: function(){ |
|
| 76 | var $this = this, |
|
| 77 | lastPageY; |
|
| 78 | ||
| 79 | this.$el.on('mousedown.drag', function(e) { |
|
| 80 | lastPageY = e.pageY; |
|
| 81 | $this.$el.add(document.body).addClass('mivhak-scrollbar-grabbed'); |
|
| 82 | $(document).on('mousemove.drag', drag).on('mouseup.drag', stop); |
|
| 83 | return false; |
|
| 84 | }); |
|
| 85 | ||
| 86 | function drag(e){ |
|
| 87 | var delta = e.pageY - lastPageY, |
|
| 88 | didScroll; |
|
| 89 | ||
| 90 | // Bail if the mouse hasn't moved |
|
| 91 | if(!delta) return; |
|
| 92 | ||
| 93 | lastPageY = e.pageY; |
|
| 94 | ||
| 95 | raf(function(){ |
|
| 96 | didScroll = $this.doScroll(delta > 0 ? 'down' : 'up', Math.abs(delta*getEditorHeight($this.$inner)/$this.$outer.parent().height())); |
|
| 97 | if(0 !== didScroll) $this.moveBar(); |
|
| 98 | }); |
|
| 99 | } |
|
| 100 | ||
| 101 | function stop() { |
|
| 102 | $this.$el.add(document.body).removeClass('mivhak-scrollbar-grabbed'); |
|
| 103 | $(document).off("mousemove.drag mouseup.drag"); |
|
| 104 | } |
|
| 105 | }, |
|
| 106 | moveBar: function() { |
|
| 107 | this.$el.css({ |
|
| 108 | top: (this.state.b-this.state.d)/(this.state.a-this.state.c)*this.state.t + 'px' |
|
| 109 | }); |
|
| 110 | }, |
|
| 111 | ||
| 112 | /** |
|
| 113 | * Scrolls the editor element in the direction given, provided that there |
|
| 114 | * is remaining scroll space |
|
| 115 | * @param {string} dir |
|
| 116 | * @param {int} delta |
|
| 117 | */ |
|
| 118 | doScroll: function(dir, delta) { |
|
| 119 | var s = this.state, |
|
| 120 | remaining, |
|
| 121 | didScroll; |
|
| 122 | ||
| 123 | if('up' === dir) |
|
| 124 | { |
|
| 125 | remaining = s.t; |
|
| 126 | didScroll = remaining > 0 ? Math.min(remaining,delta) : 0; |
|
| 127 | s.t -= didScroll; |
|
| 128 | } |
|
| 129 | if('down' === dir) |
|
| 130 | { |
|
| 131 | remaining = this.getDifference() - s.t; |
|
| 132 | didScroll = remaining > 0 ? Math.min(remaining,delta) : 0; |
|
| 133 | s.t += didScroll; |
|
| 134 | } |
|
| 135 | ||
| 136 | this.$inner.css({top: -s.t}); |
|
| 137 | return didScroll; |
|
| 138 | }, |
|
| 139 | ||
| 140 | /** |
|
| 141 | * Returns the difference between the containing div and the editor div |
|
| 142 | */ |
|
| 143 | getDifference: function() |
|
| 144 | { |
|
| 145 | return this.state.a - this.state.c; |
|
| 146 | } |
|
| 147 | } |
|
| 148 | }); |
|
| @@ 1-140 (lines=140) @@ | ||
| 1 | Mivhak.component('horizontal-scrollbar', { |
|
| 2 | template: '<div class="mivhak-scrollbar mivhak-h-scrollbar"><div class="mivhak-scrollbar-thumb"></div></div>', |
|
| 3 | props: { |
|
| 4 | editor: null, |
|
| 5 | $inner: null, |
|
| 6 | $outer: null, |
|
| 7 | mivhakInstance: null, |
|
| 8 | minWidth: 50, |
|
| 9 | state: { |
|
| 10 | a: 0, // The total width of the editor |
|
| 11 | b: 0, // The width of the viewport, excluding padding |
|
| 12 | c: 0, // The width of the viewport, including padding |
|
| 13 | d: 0, // The calculated width of the thumb |
|
| 14 | l: 0 // The current left offset of the viewport |
|
| 15 | }, |
|
| 16 | initialized: false |
|
| 17 | }, |
|
| 18 | methods: { |
|
| 19 | initialize: function() { |
|
| 20 | if(!this.initialized) |
|
| 21 | { |
|
| 22 | this.initialized = true; |
|
| 23 | this.dragDealer(); |
|
| 24 | var $this = this; |
|
| 25 | $(window).resize(function(){ |
|
| 26 | if(!$this.mivhakInstance.state.lineWrap) |
|
| 27 | $this.refresh(); |
|
| 28 | }); |
|
| 29 | } |
|
| 30 | this.refresh(); |
|
| 31 | }, |
|
| 32 | updateState: function() { |
|
| 33 | var oldState = $.extend({}, this.state); |
|
| 34 | this.state.a = this.getEditorWidth(); |
|
| 35 | this.state.b = this.$outer.parent().width(); |
|
| 36 | this.state.c = this.state.b - this.mivhakInstance.options.padding*2; |
|
| 37 | this.state.d = Math.max(this.state.c*this.state.b/this.state.a,this.minWidth); |
|
| 38 | this.state.l *= this.state.a/Math.max(oldState.a,1); // Math.max used to prevent division by zero |
|
| 39 | return this.state.a !== oldState.a || this.state.b !== oldState.b; |
|
| 40 | }, |
|
| 41 | refresh: function() { |
|
| 42 | var $this = this, oldLeft = this.state.l; |
|
| 43 | raf(function(){ |
|
| 44 | if($this.updateState()) |
|
| 45 | { |
|
| 46 | if($this.getDifference() > 0) |
|
| 47 | { |
|
| 48 | $this.doScroll('left',oldLeft-$this.state.l); |
|
| 49 | $this.$el.css({width: $this.state.d + 'px', left: 0}); |
|
| 50 | $this.moveBar(); |
|
| 51 | } |
|
| 52 | else |
|
| 53 | { |
|
| 54 | $this.doScroll('left',$this.state.l); |
|
| 55 | $this.$el.css({width: 0}); |
|
| 56 | } |
|
| 57 | } |
|
| 58 | }); |
|
| 59 | }, |
|
| 60 | dragDealer: function(){ |
|
| 61 | var $this = this, |
|
| 62 | lastPageX; |
|
| 63 | ||
| 64 | this.$el.on('mousedown.drag', function(e) { |
|
| 65 | lastPageX = e.pageX; |
|
| 66 | $this.$el.add(document.body).addClass('mivhak-scrollbar-grabbed'); |
|
| 67 | $(document).on('mousemove.drag', drag).on('mouseup.drag', stop); |
|
| 68 | return false; |
|
| 69 | }); |
|
| 70 | ||
| 71 | function drag(e){ |
|
| 72 | var delta = e.pageX - lastPageX, |
|
| 73 | didScroll; |
|
| 74 | ||
| 75 | // Bail if the mouse hasn't moved |
|
| 76 | if(!delta) return; |
|
| 77 | ||
| 78 | lastPageX = e.pageX; |
|
| 79 | ||
| 80 | raf(function(){ |
|
| 81 | didScroll = $this.doScroll(delta > 0 ? 'right' : 'left', Math.abs(delta*$this.getEditorWidth()/$this.$outer.parent().width())); |
|
| 82 | if(0 !== didScroll) $this.moveBar(); |
|
| 83 | }); |
|
| 84 | } |
|
| 85 | ||
| 86 | function stop() { |
|
| 87 | $this.$el.add(document.body).removeClass('mivhak-scrollbar-grabbed'); |
|
| 88 | $(document).off("mousemove.drag mouseup.drag"); |
|
| 89 | } |
|
| 90 | }, |
|
| 91 | moveBar: function() { |
|
| 92 | this.$el.css({ |
|
| 93 | left: (this.state.b-this.state.d)/(this.state.a-this.state.c)*this.state.l + 'px' |
|
| 94 | }); |
|
| 95 | }, |
|
| 96 | ||
| 97 | /** |
|
| 98 | * Scrolls the editor element in the direction given, provided that there |
|
| 99 | * is remaining scroll space |
|
| 100 | * @param {string} dir |
|
| 101 | * @param {int} delta |
|
| 102 | */ |
|
| 103 | doScroll: function(dir, delta) { |
|
| 104 | var s = this.state, |
|
| 105 | remaining, |
|
| 106 | didScroll; |
|
| 107 | ||
| 108 | if('left' === dir) |
|
| 109 | { |
|
| 110 | remaining = s.l; |
|
| 111 | didScroll = remaining > 0 ? Math.min(remaining,delta) : 0; |
|
| 112 | s.l -= didScroll; |
|
| 113 | } |
|
| 114 | if('right' === dir) |
|
| 115 | { |
|
| 116 | remaining = this.getDifference() - s.l; |
|
| 117 | didScroll = remaining > 0 ? Math.min(remaining,delta) : 0; |
|
| 118 | s.l += didScroll; |
|
| 119 | } |
|
| 120 | ||
| 121 | this.$inner.find('.ace_content').css({'margin-left': -s.l}); |
|
| 122 | return didScroll; |
|
| 123 | }, |
|
| 124 | ||
| 125 | /** |
|
| 126 | * Returns the difference between the containing div and the editor div |
|
| 127 | */ |
|
| 128 | getDifference: function() |
|
| 129 | { |
|
| 130 | return this.state.a - this.state.c; |
|
| 131 | }, |
|
| 132 | ||
| 133 | /** |
|
| 134 | * Calculate the editor's width based on the number of lines |
|
| 135 | */ |
|
| 136 | getEditorWidth: function() { |
|
| 137 | return this.$inner.find('.ace_content').width(); |
|
| 138 | } |
|
| 139 | } |
|
| 140 | }); |
|