@@ 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 | }); |