@@ -8,11 +8,30 @@ var FontMetrics = require('FontMetrics'),
88 * Simple plain-text text editor using html5 canvas.
99 * @constructor
1010 */
11- var CanvasTextEditor = function ( doc ) {
11+ var CanvasTextEditor = function ( doc , options ) {
1212 this . _document = doc || ( new Document ) ;
13- this . _metrics = new FontMetrics ( '"Courier New", Courier, monospace' , 14 ) ;
13+
14+ this . options = {
15+ textColor : 'WindowText' ,
16+ backgroundColor : 'Window' ,
17+ selectionColor : 'Highlight' ,
18+ focusColor : '#09f' ,
19+ fontFamily : '"Courier New", Courier, monospace' ,
20+ fontSize : 14 ,
21+ padding : 5 ,
22+ width : 640 ,
23+ height : 480
24+ } ;
25+
26+ if ( typeof options === 'object' ) {
27+ for ( key in options ) {
28+ this . options [ key ] = options [ key ] ;
29+ }
30+ }
31+
32+ this . _metrics = new FontMetrics ( this . options . fontFamily , this . options . fontSize ) ;
1433 this . _createWrapper ( ) ;
15- this . _selection = new Selection ( this ) ;
34+ this . _selection = new Selection ( this , this . options . textColor ) ;
1635 this . _selection . onchange = this . selectionChange . bind ( this ) ;
1736 this . _createCanvas ( ) ;
1837 this . _createInput ( ) ;
@@ -24,6 +43,18 @@ var CanvasTextEditor = function(doc) {
2443
2544module . exports = CanvasTextEditor ;
2645
46+ /**
47+ * Top offset in lines
48+ * @type {Number }
49+ */
50+ CanvasTextEditor . prototype . _scrollTop = 0 ;
51+
52+ /**
53+ * Left offset in characters
54+ * @type {Number }
55+ */
56+ CanvasTextEditor . prototype . _scrollLeft = 0 ;
57+
2758/**
2859 * Determines if current browser is Opera
2960 * @type {Boolean }
@@ -77,6 +108,22 @@ CanvasTextEditor.prototype.getSelection = function() {
77108 return this . _selection ;
78109} ;
79110
111+ /**
112+ * Returns current top offset
113+ * @return {number }
114+ */
115+ CanvasTextEditor . prototype . scrollTop = function ( ) {
116+ return this . _scrollTop ;
117+ } ;
118+
119+ /**
120+ * Returns current left offset
121+ * @return {number }
122+ */
123+ CanvasTextEditor . prototype . scrollLeft = function ( ) {
124+ return this . _scrollLeft ;
125+ } ;
126+
80127/**
81128 * Handles selection change
82129 */
@@ -95,6 +142,7 @@ CanvasTextEditor.prototype.selectionChange = function() {
95142 }
96143 }
97144
145+ this . _checkScroll ( ) ;
98146 this . setInputText ( selectedText , true ) ;
99147
100148 // Updating canvas to show selection
@@ -110,8 +158,8 @@ CanvasTextEditor.prototype._createWrapper = function() {
110158 this . wrapper . className = this . className ;
111159 this . wrapper . style . display = 'inline-block' ;
112160 this . wrapper . style . position = 'relative' ;
113- this . wrapper . style . backgroundColor = '#eee' ;
114- this . wrapper . style . border = '5px solid #eee' ;
161+ this . wrapper . style . backgroundColor = this . options . backgroundColor ;
162+ this . wrapper . style . border = this . options . padding + 'px solid ' + this . options . backgroundColor ;
115163 this . wrapper . style . overflow = 'hidden' ;
116164 this . wrapper . tabIndex = 0 ; // tabindex is necessary to get focus
117165 this . wrapper . addEventListener ( 'focus' , this . focus . bind ( this ) , false ) ;
@@ -125,11 +173,32 @@ CanvasTextEditor.prototype._createCanvas = function() {
125173 this . canvas = document . createElement ( 'canvas' ) ;
126174 this . canvas . style . display = 'block' ;
127175 this . context = this . canvas . getContext ( '2d' ) ;
128- this . resize ( 640 , 480 ) ;
176+ this . resize ( this . options . width , this . options . height ) ;
129177 this . render ( ) ;
130178 this . wrapper . appendChild ( this . canvas ) ;
131179} ;
132180
181+ /**
182+ * Makes sure that cursor is visible
183+ * @return {[type] } [description]
184+ */
185+ CanvasTextEditor . prototype . _checkScroll = function ( ) {
186+ var maxHeight = Math . ceil ( this . canvas . height / this . _metrics . getHeight ( ) ) - 1 ,
187+ maxWidth = Math . ceil ( this . canvas . width / this . _metrics . getWidth ( ) ) - 1 ,
188+ cursorPosition = this . _selection . getPosition ( ) ;
189+ if ( cursorPosition [ 0 ] > this . _scrollLeft + maxWidth ) {
190+ this . _scrollLeft = cursorPosition [ 0 ] - maxWidth ;
191+ } else if ( cursorPosition [ 0 ] < this . _scrollLeft ) {
192+ this . _scrollLeft = cursorPosition [ 0 ] ;
193+ }
194+ if ( cursorPosition [ 1 ] > this . _scrollTop + maxHeight ) {
195+ this . _scrollTop = cursorPosition [ 1 ] - maxHeight ;
196+ } else if ( cursorPosition [ 1 ] < this . _scrollTop ) {
197+ this . _scrollTop = cursorPosition [ 1 ] ;
198+ }
199+ this . _selection . updateCursorStyle ( ) ;
200+ } ;
201+
133202/**
134203 * Renders document onto the canvas
135204 * @return {[type] } [description]
@@ -138,7 +207,7 @@ CanvasTextEditor.prototype.render = function() {
138207 var baselineOffset = this . _metrics . getBaseline ( ) ,
139208 lineHeight = this . _metrics . getHeight ( ) ,
140209 characterWidth = this . _metrics . getWidth ( ) ,
141- maxHeight = Math . ceil ( 640 / lineHeight ) ,
210+ maxHeight = Math . ceil ( this . canvas . height / lineHeight ) ,
142211 lineCount = this . _document . getLineCount ( ) ,
143212 selectionRanges = this . _selection . lineRanges ( ) ,
144213 selectionWidth = 0 ;
@@ -147,17 +216,17 @@ CanvasTextEditor.prototype.render = function() {
147216 if ( lineCount < maxHeight ) maxHeight = lineCount ;
148217
149218 // Clearing previous iteration
150- this . context . fillStyle = '#eee' ;
219+ this . context . fillStyle = this . options . backgroundColor ;
151220 this . context . fillRect ( 0 , 0 , this . canvas . width , this . canvas . height ) ;
152- this . context . fillStyle = '#000' ;
221+ this . context . fillStyle = this . options . textColor ;
153222
154223 // Looping over document lines
155- for ( var i = 0 ; i < maxHeight ; ++ i ) {
156- var topOffset = lineHeight * i ;
224+ for ( var i = this . _scrollTop ; i < maxHeight + this . _scrollTop ; ++ i ) {
225+ var topOffset = lineHeight * ( i - this . _scrollTop ) ;
157226
158227 // Rendering selection for this line if one is present
159228 if ( selectionRanges [ i ] ) {
160- this . context . fillStyle = '#cce6ff' ;
229+ this . context . fillStyle = this . options . selectionColor ;
161230
162231 // Check whether we should select to the end of the line or not
163232 if ( selectionRanges [ i ] [ 1 ] === true ) {
@@ -168,19 +237,19 @@ CanvasTextEditor.prototype.render = function() {
168237
169238 // Drawing selection
170239 this . context . fillRect (
171- selectionRanges [ i ] [ 0 ] * characterWidth ,
172- i * lineHeight ,
240+ ( selectionRanges [ i ] [ 0 ] - this . _scrollLeft ) * characterWidth ,
241+ topOffset ,
173242 selectionWidth ,
174243 lineHeight
175244 )
176245
177246 // Restoring fill color for the text
178- this . context . fillStyle = '#000' ;
247+ this . context . fillStyle = this . options . textColor ;
179248 }
180249
181250 // Drawing text
182251 this . context . fillText (
183- this . _document . getLine ( i ) , 0 , topOffset + baselineOffset
252+ this . _document . getLine ( i ) . slice ( this . _scrollLeft ) , 0 , topOffset + baselineOffset
184253 ) ;
185254 }
186255} ;
@@ -291,7 +360,7 @@ CanvasTextEditor.prototype.deleteCharAtCurrentPosition = function(forward) {
291360 * @private
292361 */
293362CanvasTextEditor . prototype . _inputFocus = function ( ) {
294- this . wrapper . style . outline = '1px solid #09f' ;
363+ this . wrapper . style . outline = '1px solid ' + this . options . focusColor ;
295364 this . _selection . setVisible ( true ) ;
296365} ;
297366
@@ -338,10 +407,10 @@ CanvasTextEditor.prototype.resize = function(width, height) {
338407CanvasTextEditor . prototype . keydown = function ( e ) {
339408 var handled = true ;
340409 switch ( e . keyCode ) {
341- case 8 : // backspace
410+ case 8 : // Backspace
342411 this . deleteCharAtCurrentPosition ( false ) ;
343412 break ;
344- case 46 : // delete
413+ case 46 : // Delete
345414 this . deleteCharAtCurrentPosition ( true ) ;
346415 break ;
347416 case 13 : // Enter
@@ -353,7 +422,7 @@ CanvasTextEditor.prototype.keydown = function(e) {
353422 case 38 : // Up arrow
354423 this . _selection . moveUp ( 1 , this . shiftPressed ) ;
355424 break ;
356- case 39 : // Up arrow
425+ case 39 : // Right arrow
357426 this . _selection . moveRight ( 1 , this . shiftPressed ) ;
358427 break ;
359428 case 40 : // Down arrow
0 commit comments