abcjs 6.5.1 → 6.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abcjs",
3
- "version": "6.5.1",
3
+ "version": "6.6.0",
4
4
  "description": "Renderer for abc music notation",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -12,6 +12,7 @@ var TimingCallbacks = function(target, params) {
12
12
  self.lineEndCallback = params.lineEndCallback; // This is called when the end of a line is approaching.
13
13
  self.lineEndAnticipation = params.lineEndAnticipation ? parseInt(params.lineEndAnticipation, 10) : 0; // How many milliseconds before the end should the call happen.
14
14
  self.beatSubdivisions = params.beatSubdivisions ? parseInt(params.beatSubdivisions, 10) : 1; // how many callbacks per beat is desired.
15
+ if (!self.beatSubdivisions) self.beatSubdivisions = 1
15
16
  self.joggerTimer = null;
16
17
 
17
18
  self.replaceTarget = function(newTarget) {
@@ -38,7 +39,73 @@ var TimingCallbacks = function(target, params) {
38
39
  // noteTimings contains an array of events sorted by time. Events that happen at the same time are in the same element of the array.
39
40
  self.millisecondsPerBeat = 1000 / (self.qpm / 60) / self.beatSubdivisions;
40
41
  self.lastMoment = self.noteTimings[self.noteTimings.length-1].milliseconds;
41
- self.totalBeats = Math.round(self.lastMoment / self.millisecondsPerBeat);
42
+
43
+ // For irregular time sigs that specify the beat divisions (that is, something like `M: 2+3/8`)
44
+ // Then the beat is not a regular pulse. To keep most of this logic easy, the beat will be specified
45
+ // as half as long, but the callback will happen according to the pattern. That is,
46
+ // for bpm=60 at the above time signature, internally the beat callback will happen at 120 bpm, but
47
+ // the callback function will be called at 2 sub beats, then 3 sub beats, etc.
48
+ // The beat number will be an integer for all of them and count up by one each time.
49
+ var meter = newTarget.getMeter()
50
+ var irregularMeter = ''
51
+ if (meter && meter.type === "specified" && meter.value && meter.value.length > 0 && meter.value[0].num.indexOf('+') > 0)
52
+ irregularMeter = meter.value[0].num
53
+ // for subdivisions = 1, then this should contain only whole numbers and the callbacks are irregular
54
+ // for subdivisions = 2, then this should be 1/8 notes. The beats should be something like: 0, 0.5, 1, 1.33, 1.66 2 (For M:2+3/8)
55
+ // etc for more subdivisions - they are just multiplied
56
+ self.beatStarts = []
57
+ if (irregularMeter) {
58
+ var measureLength = self.noteTimings[self.noteTimings.length-1].millisecondsPerMeasure
59
+ var numMeasures = self.lastMoment / measureLength
60
+ var parts = irregularMeter.split("+")
61
+ for (var i = 0; i < parts.length; i++)
62
+ parts[i] = parseInt(parts[i],10) / 2 // since we count a beat as a quarter note, but these numbers refer to 1/8 notes, we convert the beat length
63
+ var currentTs = 0
64
+ var beatNumber = 0
65
+ // For the input: parts = [ 1, 1.5 ] and beatSubdivisions = 2
66
+ // beatNumbers = 0 0.5 1 1.33 1.67 2 2.5 3 3.33 3.67 ...
67
+ // when part=1 then there is 1 extra sub beat and it is 0.5
68
+ // when part=1.5 then there are 2 extra sub beats at 0.33 and 0.67
69
+ //
70
+ // beatSubdivision | numFor1 | numFor1.5
71
+ // 2 | 2 | 3
72
+ // 3 | 3 | 4.5
73
+ // 4 | 4 | 6
74
+ for (var measureNumber = 0; measureNumber < numMeasures; measureNumber++) {
75
+ var measureStartTs = measureNumber * measureLength
76
+ var subBeatCounter = 0
77
+ for (var kk = 0; kk < parts.length; kk++) {
78
+ var beatLength = parts[kk] // This is either 1 or 1.5 (how many quarter notes in this beat)
79
+ if (self.beatSubdivisions === 1) {
80
+ if (self.beatSubdivisions === 1)
81
+ if (currentTs < self.lastMoment) {
82
+ self.beatStarts.push({b: beatNumber, ts: currentTs})
83
+ }
84
+ currentTs += beatLength * self.millisecondsPerBeat
85
+ } else {
86
+ var numDivisions = beatLength * self.beatSubdivisions
87
+ for (var k = 0; k < Math.floor(numDivisions); k++) {
88
+ var subBeat = k / numDivisions
89
+ var ts = Math.round(measureStartTs + subBeatCounter * self.millisecondsPerBeat)
90
+ if (ts < self.lastMoment) {
91
+ self.beatStarts.push({b: beatNumber + subBeat, ts: ts})
92
+ }
93
+ subBeatCounter++
94
+ }
95
+ }
96
+ beatNumber++
97
+ }
98
+ }
99
+ self.beatStarts.push({b: numMeasures * parts.length, ts: self.lastMoment})
100
+ self.totalBeats = self.beatStarts.length
101
+ } else {
102
+ self.totalBeats = Math.round(self.lastMoment / self.millisecondsPerBeat);
103
+ // Add one so the last beat is the last moment
104
+ for (var j = 0; j < self.totalBeats+1; j++) {
105
+ self.beatStarts.push({b: j/self.beatSubdivisions, ts: Math.round(j * self.millisecondsPerBeat)})
106
+ }
107
+ }
108
+ //console.log({lastMoment: self.lastMoment, beatStarts: self.beatStarts})
42
109
  };
43
110
 
44
111
  self.replaceTarget(target);
@@ -49,11 +116,11 @@ var TimingCallbacks = function(target, params) {
49
116
  if (self.lastTimestamp === timestamp)
50
117
  return; // If there are multiple seeks or other calls, then we can easily get multiple callbacks for the same instant.
51
118
  self.lastTimestamp = timestamp;
52
- if (!self.startTime) {
53
- self.startTime = timestamp;
54
- }
55
119
 
56
120
  if (!self.isPaused && self.isRunning) {
121
+ if (!self.startTime) {
122
+ self.startTime = timestamp;
123
+ }
57
124
  self.currentTime = timestamp - self.startTime;
58
125
  self.currentTime += 16; // Add a little slop because this function isn't called exactly.
59
126
  while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
@@ -73,8 +140,9 @@ var TimingCallbacks = function(target, params) {
73
140
  }
74
141
  if (self.currentTime < self.lastMoment) {
75
142
  requestAnimationFrame(self.doTiming);
76
- if (self.currentBeat * self.millisecondsPerBeat < self.currentTime) {
143
+ if (self.currentBeat < self.beatStarts.length && self.beatStarts[self.currentBeat].ts <= self.currentTime) {
77
144
  var ret = self.doBeatCallback(timestamp);
145
+ self.currentBeat++
78
146
  if (ret !== null)
79
147
  self.currentTime = ret;
80
148
  }
@@ -82,6 +150,7 @@ var TimingCallbacks = function(target, params) {
82
150
  // Because of timing issues (for instance, if the browser tab isn't active), the beat callbacks might not have happened when they are supposed to. To keep the client programs from having to deal with that, this will keep calling the loop until all of them have been sent.
83
151
  if (self.beatCallback) {
84
152
  var ret2 = self.doBeatCallback(timestamp);
153
+ self.currentBeat++
85
154
  if (ret2 !== null)
86
155
  self.currentTime = ret2;
87
156
  requestAnimationFrame(self.doTiming);
@@ -178,15 +247,15 @@ var TimingCallbacks = function(target, params) {
178
247
 
179
248
  var thisStartTime = self.startTime; // the beat callback can call seek and change the position from beneath us.
180
249
  self.beatCallback(
181
- self.currentBeat / self.beatSubdivisions,
250
+ self.beatStarts[self.currentBeat].b,
182
251
  self.totalBeats / self.beatSubdivisions,
183
252
  self.lastMoment,
184
253
  position,
185
254
  debugInfo);
186
255
  if (thisStartTime !== self.startTime) {
187
256
  return timestamp - self.startTime;
188
- } else
189
- self.currentBeat++;
257
+ } // else
258
+ // self.currentBeat++;
190
259
  }
191
260
  return null;
192
261
  };
@@ -285,7 +354,6 @@ var TimingCallbacks = function(target, params) {
285
354
  var now = performance.now();
286
355
  self.startTime = now - self.currentTime;
287
356
 
288
- var oldEvent = self.currentEvent;
289
357
  self.currentEvent = 0;
290
358
  while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
291
359
  self.currentEvent++;
@@ -298,10 +366,18 @@ var TimingCallbacks = function(target, params) {
298
366
  }
299
367
  }
300
368
 
369
+ //console.log({jump:self.currentTime})
301
370
  var oldBeat = self.currentBeat;
302
- self.currentBeat = Math.floor(self.currentTime / self.millisecondsPerBeat);
303
- if (self.beatCallback && oldBeat !== self.currentBeat) // If the movement caused the beat to change, then immediately report it to the client.
304
- self.doBeatCallback(self.startTime+self.currentTime);
371
+ for (self.currentBeat = 0; self.currentBeat < self.beatStarts.length; self.currentBeat++) {
372
+ if (self.beatStarts[self.currentBeat].ts > self.currentTime)
373
+ break
374
+ }
375
+ self.currentBeat--
376
+ if (self.beatCallback && oldBeat !== self.currentBeat) {
377
+ // If the movement caused the beat to change, then immediately report it to the client.
378
+ self.doBeatCallback(self.startTime + self.currentTime);
379
+ self.currentBeat++
380
+ }
305
381
 
306
382
  if (self.eventCallback && self.currentEvent >= 0 && self.noteTimings[self.currentEvent].type === 'event')
307
383
  self.eventCallback(self.noteTimings[self.currentEvent]);
@@ -328,4 +404,3 @@ function getLineEndTimings(timings, anticipation) {
328
404
  }
329
405
 
330
406
  module.exports = TimingCallbacks;
331
-
@@ -1,21 +1,26 @@
1
1
  // All these keys have the same number of accidentals
2
2
  var keys = {
3
- 'C': { modes: ['CMaj', 'Amin', 'Am', 'GMix', 'DDor', 'EPhr', 'FLyd', 'BLoc'], stepsFromC: 0 },
4
- 'Db': { modes: ['DbMaj', 'Bbmin', 'Bbm', 'AbMix', 'EbDor', 'FPhr', 'GbLyd', 'CLoc'], stepsFromC: 1 },
5
- 'D': { modes: ['DMaj', 'Bmin', 'Bm', 'AMix', 'EDor', 'F#Phr', 'GLyd', 'C#Loc'], stepsFromC: 2 },
6
- 'Eb': { modes: ['EbMaj', 'Cmin', 'Cm', 'BbMix', 'FDor', 'GPhr', 'AbLyd', 'DLoc'], stepsFromC: 3 },
7
- 'E': { modes: ['EMaj', 'C#min', 'C#m', 'BMix', 'F#Dor', 'G#Phr', 'ALyd', 'D#Loc'], stepsFromC: 4 },
8
- 'F': { modes: ['FMaj', 'Dmin', 'Dm', 'CMix', 'GDor', 'APhr', 'BbLyd', 'ELoc'], stepsFromC: 5 },
9
- 'Gb': { modes: ['GbMaj', 'Ebmin', 'Ebm', 'DbMix', 'AbDor', 'BbPhr', 'CbLyd', 'FLoc'], stepsFromC: 6 },
10
- 'G': { modes: ['GMaj', 'Emin', 'Em', 'DMix', 'ADor', 'BPhr', 'CLyd', 'F#Loc'], stepsFromC: 7 },
11
- 'Ab': { modes: ['AbMaj', 'Fmin', 'Fm', 'EbMix', 'BbDor', 'CPhr', 'DbLyd', 'GLoc'], stepsFromC: 8 },
12
- 'A': { modes: ['AMaj', 'F#min', 'F#m', 'EMix', 'BDor', 'C#Phr', 'DLyd', 'G#Loc'], stepsFromC: 9 },
13
- 'Bb': { modes: ['BbMaj', 'Gmin', 'Gm', 'FMix', 'CDor', 'DPhr', 'EbLyd', 'ALoc'], stepsFromC: 10 },
14
- 'B': { modes: ['BMaj', 'G#min', 'G#m', 'F#Mix', 'C#Dor', 'D#Phr', 'ELyd', 'A#Loc'], stepsFromC: 11 },
3
+ 'C': { modes: ['CMaj', 'CIon', 'Amin', 'AAeo', 'Am', 'GMix', 'DDor', 'EPhr', 'FLyd', 'BLoc'], stepsFromC: 0 },
4
+ 'Db': { modes: ['DbMaj', 'DbIon', 'Bbmin', 'BbAeo', 'Bbm', 'AbMix', 'EbDor', 'FPhr', 'GbLyd', 'CLoc'], stepsFromC: 1 },
5
+ 'D': { modes: ['DMaj', 'DIon', 'Bmin', 'BAeo', 'Bm', 'AMix', 'EDor', 'F#Phr', 'GLyd', 'C#Loc'], stepsFromC: 2 },
6
+ 'Eb': { modes: ['EbMaj', 'EbIon', 'Cmin', 'CAeo', 'Cm', 'BbMix', 'FDor', 'GPhr', 'AbLyd', 'DLoc'], stepsFromC: 3 },
7
+ 'E': { modes: ['EMaj', 'EIon', 'C#min', 'C#Aeo', 'C#m', 'BMix', 'F#Dor', 'G#Phr', 'ALyd', 'D#Loc'], stepsFromC: 4 },
8
+ 'F': { modes: ['FMaj', 'FIon', 'Dmin', 'DAeo', 'Dm', 'CMix', 'GDor', 'APhr', 'BbLyd', 'ELoc'], stepsFromC: 5 },
9
+ 'Gb': { modes: ['GbMaj', 'GbIon', 'Ebmin', 'EbAeo', 'Ebm', 'DbMix', 'AbDor', 'BbPhr', 'CbLyd', 'FLoc'], stepsFromC: 6 },
10
+ 'G': { modes: ['GMaj', 'GIon', 'Emin', 'EAeo', 'Em', 'DMix', 'ADor', 'BPhr', 'CLyd', 'F#Loc'], stepsFromC: 7 },
11
+ 'Ab': { modes: ['AbMaj', 'AbIon', 'Fmin', 'FAeo', 'Fm', 'EbMix', 'BbDor', 'CPhr', 'DbLyd', 'GLoc'], stepsFromC: 8 },
12
+ 'A': { modes: ['AMaj', 'AIon', 'F#min', 'F#Aeo', 'F#m', 'EMix', 'BDor', 'C#Phr', 'DLyd', 'G#Loc'], stepsFromC: 9 },
13
+ 'Bb': { modes: ['BbMaj', 'BbIon', 'Gmin', 'GAeo', 'Gm', 'FMix', 'CDor', 'DPhr', 'EbLyd', 'ALoc'], stepsFromC: 10 },
14
+ 'B': { modes: ['BMaj', 'BIon', 'G#min', 'G#Aeo', 'G#m', 'F#Mix', 'C#Dor', 'D#Phr', 'ELyd', 'A#Loc'], stepsFromC: 11 },
15
15
  // Enharmonic keys
16
- 'C#': { modes: ['C#Maj', 'A#min', 'A#m', 'G#Mix', 'D#Dor', 'E#Phr', 'F#Lyd', 'B#Loc'], stepsFromC: 1 },
17
- 'F#': { modes: ['F#Maj', 'D#min', 'D#m', 'C#Mix', 'G#Dor', 'A#Phr', 'BLyd', 'E#Loc'], stepsFromC: 6 },
18
- 'Cb': { modes: ['CbMaj', 'Abmin', 'Abm', 'GbMix', 'DbDor', 'EbPhr', 'FbLyd', 'BbLoc'], stepsFromC: 11 },
16
+ 'C#': { modes: ['C#Maj', 'C#Ion', 'A#min', 'A#Aeo', 'A#m', 'G#Mix', 'D#Dor', 'E#Phr', 'F#Lyd', 'B#Loc'], stepsFromC: 1 },
17
+ 'F#': { modes: ['F#Maj', 'F#Ion', 'D#min', 'D#Aeo', 'D#m', 'C#Mix', 'G#Dor', 'A#Phr', 'BLyd', 'E#Loc'], stepsFromC: 6 },
18
+ 'Cb': { modes: ['CbMaj', 'CbIon', 'Abmin', 'AbAeo', 'Abm', 'GbMix', 'DbDor', 'EbPhr', 'FbLyd', 'BbLoc'], stepsFromC: 11 },
19
+ }
20
+
21
+ var modeNames = ['maj', 'ion', 'min', 'aeo', 'm', 'mix', 'dor', 'phr', 'lyd', 'loc']
22
+ function isLegalMode(mode) {
23
+ return modeNames.indexOf(mode.toLowerCase()) >= 0
19
24
  }
20
25
 
21
26
  var keyReverse = null
@@ -42,7 +47,7 @@ function relativeMajor(key) {
42
47
  createKeyReverse()
43
48
  }
44
49
  // get the key portion itself - there might be other stuff, like extra sharps and flats, or the mode written out.
45
- var mode = key.toLowerCase().match(/([a-g][b#]?)(maj|min|mix|dor|phr|lyd|loc|m)?/)
50
+ var mode = key.toLowerCase().match(/([a-g][b#]?)(maj|ion|min|aeo|mix|dor|phr|lyd|loc|m)?/);
46
51
  if (!mode || !mode[2])
47
52
  return key;
48
53
  mode = mode[1] + mode[2]
@@ -60,10 +65,10 @@ function relativeMode(majorKey, mode) {
60
65
  return majorKey;
61
66
  if (mode === '')
62
67
  return majorKey;
63
- var match = mode.toLowerCase().match(/^(maj|min|mix|dor|phr|lyd|loc|m)/)
68
+ var match = mode.toLowerCase().match(/^(maj|ion|min|aeo|mix|dor|phr|lyd|loc|m)/);
64
69
  if (!match)
65
70
  return majorKey
66
- var regMode = match[1]
71
+ var regMode = match[1]
67
72
  for (var i = 0; i < group.modes.length; i++) {
68
73
  var thisMode = group.modes[i]
69
74
  var ind = thisMode.toLowerCase().indexOf(regMode)
@@ -89,4 +94,4 @@ function transposeKey(key, steps) {
89
94
  return key;
90
95
  }
91
96
 
92
- module.exports = {relativeMajor: relativeMajor, relativeMode: relativeMode, transposeKey: transposeKey};
97
+ module.exports = {relativeMajor: relativeMajor, relativeMode: relativeMode, transposeKey: transposeKey, isLegalMode:isLegalMode};
@@ -90,12 +90,17 @@ var Tune = function() {
90
90
  this.getBeatLength = function() {
91
91
  // This returns a fraction: for instance 1/4 for a quarter
92
92
  // There are two types of meters: compound and regular. Compound meter has 3 beats counted as one.
93
+
94
+ // Irregular meters have the beat as an 1/8 note but the tempo as a 1/4.
95
+ // That keeps it a similar tempo to 4/4 but that may or may not be generally intuitive, so that might need to change.
93
96
  var meter = this.getMeterFraction();
94
97
  var multiplier = 1;
95
98
  if (meter.num === 6 || meter.num === 9 || meter.num === 12)
96
99
  multiplier = 3;
97
100
  else if (meter.num === 3 && meter.den === 8)
98
101
  multiplier = 3;
102
+ else if (meter.den === 8 && (meter.num === 5 || meter.num === 7))
103
+ multiplier = 2
99
104
 
100
105
  return multiplier / meter.den;
101
106
  };
@@ -194,7 +199,13 @@ var Tune = function() {
194
199
  var den = 4;
195
200
  if (meter) {
196
201
  if (meter.type === 'specified') {
197
- num = parseInt(meter.value[0].num, 10);
202
+ if (meter.value && meter.value.length > 0 && meter.value[0].num.indexOf('+') > 0) {
203
+ var parts = meter.value[0].num.split('+')
204
+ num = 0;
205
+ for (var i = 0; i < parts.length; i++)
206
+ num += parseInt(parts[i],10)
207
+ } else
208
+ num = parseInt(meter.value[0].num, 10);
198
209
  den = parseInt(meter.value[0].den,10);
199
210
  } else if (meter.type === 'cut_time') {
200
211
  num = 2;
@@ -11,7 +11,7 @@ function delineTune(inputLines, options) {
11
11
  var currentTripletFont = [];
12
12
  var currentAnnotationFont = [];
13
13
  for (var i = 0; i < inputLines.length; i++) {
14
- var inputLine = inputLines[i];
14
+ var inputLine = cloneLine(inputLines[i]);
15
15
  if (inputLine.staff) {
16
16
  if (inMusicLine && !inputLine.vskip) {
17
17
  var outputLine = outputLines[outputLines.length-1];
@@ -43,73 +43,76 @@ try {
43
43
  // if we aren't in a browser, this code will crash, but it is not needed then either.
44
44
  }
45
45
 
46
- var EditArea = function(textareaid) {
47
- if (typeof textareaid === "string")
48
- this.textarea = document.getElementById(textareaid);
49
- else
50
- this.textarea = textareaid;
51
- this.initialText = this.textarea.value;
52
- this.isDragging = false;
46
+ var EditArea = function (textareaid) {
47
+ this.isEditArea = true
48
+ if (typeof textareaid === "string") {
49
+ this.textarea = document.getElementById(textareaid);
50
+ if (!this.textarea)
51
+ this.textarea = document.querySelector(textareaid)
52
+ } else
53
+ this.textarea = textareaid;
54
+ this.initialText = this.textarea.value;
55
+ this.isDragging = false;
53
56
  }
54
57
 
55
- EditArea.prototype.addSelectionListener = function(listener) {
56
- this.textarea.onmousemove = function(ev) {
57
- if (this.isDragging)
58
- listener.fireSelectionChanged();
59
- };
58
+ EditArea.prototype.addSelectionListener = function (listener) {
59
+ this.textarea.onmousemove = function (ev) {
60
+ if (this.isDragging)
61
+ listener.fireSelectionChanged();
62
+ };
60
63
  };
61
64
 
62
- EditArea.prototype.addChangeListener = function(listener) {
63
- this.changelistener = listener;
64
- this.textarea.onkeyup = function() {
65
- listener.fireChanged();
66
- };
67
- this.textarea.onmousedown = function() {
68
- this.isDragging = true;
69
- listener.fireSelectionChanged();
70
- };
71
- this.textarea.onmouseup = function() {
72
- this.isDragging = false;
73
- listener.fireChanged();
74
- };
75
- this.textarea.onchange = function() {
76
- listener.fireChanged();
77
- };
65
+ EditArea.prototype.addChangeListener = function (listener) {
66
+ this.changelistener = listener;
67
+ this.textarea.onkeyup = function () {
68
+ listener.fireChanged();
69
+ };
70
+ this.textarea.onmousedown = function () {
71
+ this.isDragging = true;
72
+ listener.fireSelectionChanged();
73
+ };
74
+ this.textarea.onmouseup = function () {
75
+ this.isDragging = false;
76
+ listener.fireChanged();
77
+ };
78
+ this.textarea.onchange = function () {
79
+ listener.fireChanged();
80
+ };
78
81
  };
79
82
 
80
83
  //TODO won't work under IE?
81
- EditArea.prototype.getSelection = function() {
82
- return {start: this.textarea.selectionStart, end: this.textarea.selectionEnd};
84
+ EditArea.prototype.getSelection = function () {
85
+ return {start: this.textarea.selectionStart, end: this.textarea.selectionEnd};
83
86
  };
84
87
 
85
- EditArea.prototype.setSelection = function(start, end) {
86
- if(this.textarea.setSelectionRange)
87
- this.textarea.setSelectionRange(start, end);
88
- else if(this.textarea.createTextRange) {
88
+ EditArea.prototype.setSelection = function (start, end) {
89
+ if (this.textarea.setSelectionRange)
90
+ this.textarea.setSelectionRange(start, end);
91
+ else if (this.textarea.createTextRange) {
89
92
  // For IE8
90
- var e = this.textarea.createTextRange();
91
- e.collapse(true);
92
- e.moveEnd('character', end);
93
- e.moveStart('character', start);
94
- e.select();
93
+ var e = this.textarea.createTextRange();
94
+ e.collapse(true);
95
+ e.moveEnd('character', end);
96
+ e.moveStart('character', start);
97
+ e.select();
95
98
  }
96
- this.textarea.focus();
99
+ this.textarea.focus();
97
100
  };
98
101
 
99
- EditArea.prototype.getString = function() {
100
- return this.textarea.value;
102
+ EditArea.prototype.getString = function () {
103
+ return this.textarea.value;
101
104
  };
102
105
 
103
- EditArea.prototype.setString = function(str) {
104
- this.textarea.value = str;
105
- this.initialText = this.getString();
106
- if (this.changelistener) {
107
- this.changelistener.fireChanged();
108
- }
106
+ EditArea.prototype.setString = function (str) {
107
+ this.textarea.value = str;
108
+ this.initialText = this.getString();
109
+ if (this.changelistener) {
110
+ this.changelistener.fireChanged();
111
+ }
109
112
  };
110
113
 
111
- EditArea.prototype.getElem = function() {
112
- return this.textarea;
114
+ EditArea.prototype.getElem = function () {
115
+ return this.textarea;
113
116
  };
114
117
 
115
118
  module.exports = EditArea;