Skip to content

Commit 397df74

Browse files
author
Dmitriy Kubyshkin
committed
Added insertion and removal of the text.
1 parent 533e1fd commit 397df74

File tree

4 files changed

+181
-11
lines changed

4 files changed

+181
-11
lines changed

lib/CanvasTextEditor.js

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,33 @@ CanvasTextEditor.prototype._createCanvas = function() {
5050
this.canvas.style.display = 'block';
5151
this.context = this.canvas.getContext('2d');
5252
this.resize(640, 480);
53+
this.render();
54+
this.wrapper.appendChild(this.canvas);
55+
};
5356

54-
// For now just very dumb implementation of rendering
57+
/**
58+
* Renders document onto the canvas
59+
* @return {[type]} [description]
60+
*/
61+
CanvasTextEditor.prototype.render = function() {
5562
var baselineOffset = this._metrics.getBaseline(),
5663
lineHeight = this._metrics.getHeight(),
5764
characterWidth = this._metrics.getWidth(),
5865
maxHeight = Math.ceil(640 / lineHeight),
5966
lineCount = this._document.getLineCount();
6067

68+
// Making sure we don't render somethign that we won't see
6169
if (lineCount < maxHeight) maxHeight = lineCount;
6270

71+
// Clearing previous iteration
72+
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
73+
74+
// Looping over document lines
6375
for(var i = 0; i < maxHeight; ++i) {
6476
this.context.fillText(
6577
this._document.getLine(i), 0, lineHeight * i + baselineOffset
6678
);
6779
}
68-
69-
this.wrapper.appendChild(this.canvas);
7080
};
7181

7282
/**
@@ -76,16 +86,54 @@ CanvasTextEditor.prototype._createCanvas = function() {
7686
CanvasTextEditor.prototype._createInput = function() {
7787
this.inputEl = document.createElement('textarea');
7888
this.inputEl.style.position = 'absolute';
79-
this.inputEl.style.top = '-100px';
89+
this.inputEl.style.top = '-10px';
8090
this.inputEl.style.height = 0;
8191
this.inputEl.style.width = 0;
92+
this.inputEl.addEventListener('input', this.handleInput.bind(this), false);
8293
this.inputEl.addEventListener('blur', this.blur.bind(this), false);
8394
this.inputEl.addEventListener('focus', this._inputFocus.bind(this), false);
8495
this.inputEl.addEventListener('keydown', this.keydown.bind(this), false);
8596
this.inputEl.tabIndex = -1; // we don't want input to get focus by tabbing
8697
this.wrapper.appendChild(this.inputEl);
8798
};
8899

100+
/**
101+
* Handles regular text input into our proxy field
102+
* @param {Event} e
103+
*/
104+
CanvasTextEditor.prototype.handleInput = function(e) {
105+
this.insertTextAtCurrentPosition(e.target.value);
106+
e.target.value = '';
107+
};
108+
109+
/**
110+
* Inserts text at the current cursor position
111+
* @param {string} text
112+
*/
113+
CanvasTextEditor.prototype.insertTextAtCurrentPosition = function(text) {
114+
var pos = this._selection.getPosition();
115+
// Inserting new text and changing position of cursor to a new one
116+
this._selection.setPosition.apply(
117+
this._selection,
118+
this._document.insertText(text, pos[0], pos[1])
119+
);
120+
this.render();
121+
};
122+
123+
/**
124+
* Deletes text at the current cursor position
125+
* @param {string} text
126+
*/
127+
CanvasTextEditor.prototype.deleteCharAtCurrentPosition = function(forward) {
128+
var pos = this._selection.getPosition();
129+
// Deleting text and changing position of cursor to a new one
130+
this._selection.setPosition.apply(
131+
this._selection,
132+
this._document.deleteChar(forward, pos[0], pos[1])
133+
);
134+
this.render();
135+
};
136+
89137
/**
90138
* Real handler code for editor gaining focus.
91139
* @private
@@ -137,6 +185,15 @@ CanvasTextEditor.prototype.resize = function(width, height) {
137185
CanvasTextEditor.prototype.keydown = function(e) {
138186
var handled = true;
139187
switch(e.keyCode) {
188+
case 8: // backspace
189+
this.deleteCharAtCurrentPosition(false);
190+
break;
191+
case 46: // delete
192+
this.deleteCharAtCurrentPosition(true);
193+
break;
194+
case 13: // Enter
195+
this.insertTextAtCurrentPosition('\n');
196+
break;
140197
case 37: // Left arrow
141198
this._selection.moveLeft();
142199
break;
@@ -152,7 +209,9 @@ CanvasTextEditor.prototype.keydown = function(e) {
152209
default:
153210
handled = false;
154211
}
155-
return !handled;
212+
if(handled) {
213+
e.preventDefault();
214+
}
156215
};
157216

158217
/**

lib/Document.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,107 @@ Document.prototype.getLength = function() {
6969
Document.prototype.charAt = function(column, row) {
7070
var row = this.storage[row];
7171
if (row) return row.charAt(column);
72+
};
73+
74+
/**
75+
* Inserts text into arbitrary position in the document
76+
* @param {string} text
77+
* @param {number} column
78+
* @param {number} row
79+
* @return {Array} new position in the document
80+
*/
81+
Document.prototype.insertText = function(text, column, row) {
82+
// First we need to split inserting text into array lines
83+
text = Document.prepareText(text);
84+
85+
// First we calculate new column position because
86+
// text array will be changed in the process
87+
var newColumn = text[text.length - 1].length;
88+
if (text.length === 1) newColumn += column;
89+
90+
// append remainder of the current line to last line in new text
91+
text[text.length - 1] += this.storage[row].substr(column);
92+
93+
// append first line of the new text to current line up to "column" position
94+
this.storage[row] = this.storage[row].substr(0, column) + text[0];
95+
96+
// now we are ready to splice other new lines
97+
// (not first and not last) into our storage
98+
var args = [row + 1, 0].concat(text.slice(1));
99+
this.storage.splice.apply(this.storage, args);
100+
101+
// Finally we calculate new position
102+
column = newColumn;
103+
row += text.length - 1;
104+
105+
return [column, row];
106+
};
107+
108+
/**
109+
* Deletes text with specified range from the document.
110+
* @param {number} startColumn
111+
* @param {number} startRow
112+
* @param {number} endColumn
113+
* @param {number} endRow
114+
*/
115+
Document.prototype.deleteRange = function(startColumn, startRow, endColumn, endRow) {
116+
117+
// Check bounds
118+
startRow >= 0 || (startRow = 0);
119+
startColumn >= 0 || (startColumn = 0);
120+
endRow < this.storage.length || (endRow = this.storage.length - 1);
121+
endColumn <= this.storage[endRow].length || (endColumn = this.storage[endRow].length);
122+
123+
// Little optimization that does nothing if there's nothing to delete
124+
if(startColumn === endColumn && startRow === endRow) {
125+
return [startColumn, startRow];
126+
}
127+
128+
// Now we append start of start row to the remainder of endRow
129+
this.storage[startRow] = this.storage[startRow].substr(0, startColumn) +
130+
this.storage[endRow].substr(endColumn);
131+
132+
// And remove everything inbetween
133+
this.storage.splice(startRow + 1, endRow - startRow);
134+
135+
// Return new position
136+
return [startColumn, startRow];
137+
};
138+
139+
/**
140+
* Deletes one char forward or backward
141+
* @param {boolean} forward
142+
* @param {number} column
143+
* @param {number} row
144+
* @return {Array} new position
145+
*/
146+
Document.prototype.deleteChar = function(forward, startColumn, startRow) {
147+
var endRow = startRow,
148+
endColumn = startColumn;
149+
150+
if (forward) {
151+
// If there are characters after cursor on this line we simple remove one
152+
if (startColumn < this.storage[startRow].trim('\n').length) {
153+
++endColumn;
154+
}
155+
// if there are rows after this one we append it
156+
else if (startRow < this.storage.length - 1) {
157+
++endRow;
158+
endColumn = 0;
159+
}
160+
}
161+
// Deleting backwards
162+
else {
163+
// If there are characters before the cursor on this line we simple remove one
164+
if (startColumn > 0) {
165+
--startColumn;
166+
}
167+
// if there are rwos before we append current to previous one
168+
else if (startRow > 0) {
169+
--startRow;
170+
startColumn = this.storage[startRow].length - 1;
171+
}
172+
}
173+
174+
return this.deleteRange(startColumn, startRow, endColumn, endRow);
72175
};

lib/Selection.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Selection.prototype.blink = function() {
4444
* @param {number?} line
4545
* @param {number?} character
4646
*/
47-
Selection.prototype.setPosition = function(line, character) {
47+
Selection.prototype.setPosition = function(character, line) {
4848
// Providing defaults for both line and character parts of position
4949
if (typeof line === 'undefined') line = this.end.line
5050
if (typeof character === 'undefined') character = this.end.character
@@ -80,13 +80,21 @@ Selection.prototype.setPosition = function(line, character) {
8080
}
8181
};
8282

83+
/**
84+
* Returns current position of the end of the selection
85+
* @return {Array}
86+
*/
87+
Selection.prototype.getPosition = function() {
88+
return [this.end.character, this.end.line];
89+
}
90+
8391
/**
8492
* Moves up specified amount of lines.
8593
* @param {number} length
8694
*/
8795
Selection.prototype.moveUp = function(length) {
8896
arguments.length || (length = 1);
89-
this.setPosition(this.end.line - length);
97+
this.setPosition(this.end.character, this.end.line - length);
9098
};
9199

92100
/**
@@ -95,7 +103,7 @@ Selection.prototype.moveUp = function(length) {
95103
*/
96104
Selection.prototype.moveDown = function(length) {
97105
arguments.length || (length = 1);
98-
this.setPosition(this.end.line + length);
106+
this.setPosition(this.end.character, this.end.line + length);
99107
};
100108

101109
/**
@@ -104,7 +112,7 @@ Selection.prototype.moveDown = function(length) {
104112
*/
105113
Selection.prototype.moveLeft = function(length) {
106114
arguments.length || (length = 1);
107-
this.setPosition(undefined, this.end.character - length);
115+
this.setPosition(this.end.character - length, this.end.line);
108116
};
109117

110118
/**
@@ -113,7 +121,7 @@ Selection.prototype.moveLeft = function(length) {
113121
*/
114122
Selection.prototype.moveRight = function(length) {
115123
arguments.length || (length = 1);
116-
this.setPosition(undefined, this.end.character + length);
124+
this.setPosition(this.end.character + length, this.end.line);
117125
};
118126

119127
/**

scripts/common.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ var project = JSON.parse(fs.readFileSync(__dirname + '/../package.json'));
66

77
// Stitch everything together
88
var package = stitch.createPackage({
9-
paths: [__dirname + '/../lib', __dirname + '/../vendor']
9+
paths: [__dirname + '/../lib']
1010
});
1111

1212
module.exports = {

0 commit comments

Comments
 (0)