@checksub_team/peaks_timeline 1.16.1 → 2.0.0-alpha.2

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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/peaks.js +4716 -4409
  3. package/peaks.js.d.ts +5 -5
  4. package/src/{timeline-axis.js → components/axis.js} +244 -244
  5. package/src/{data-retriever.js → components/data-retriever.js} +117 -117
  6. package/src/{default-segment-marker.js → components/default-segment-marker.js} +132 -132
  7. package/src/{invoker.js → components/invoker.js} +81 -81
  8. package/src/components/line-group.js +692 -0
  9. package/src/components/line-groups.js +585 -0
  10. package/src/{line-indicator.js → components/line-indicator.js} +308 -303
  11. package/src/{marker-factories.js → components/marker-factories.js} +1 -1
  12. package/src/{mode-layer.js → components/mode-layer.js} +8 -12
  13. package/src/{playhead-layer.js → components/playhead-layer.js} +3 -3
  14. package/src/{segment-marker.js → components/segment-marker.js} +2 -2
  15. package/src/{segment-shape.js → components/segment-shape.js} +508 -508
  16. package/src/{segments-group.js → components/segments-group.js} +805 -801
  17. package/src/{source-group.js → components/source-group.js} +1661 -1640
  18. package/src/{sources-layer.js → components/sources-layer.js} +716 -730
  19. package/src/{waveform-builder.js → components/waveform-builder.js} +2 -2
  20. package/src/{waveform-shape.js → components/waveform-shape.js} +214 -214
  21. package/src/keyboard-handler.js +9 -9
  22. package/src/line-handler.js +179 -0
  23. package/src/main.js +110 -71
  24. package/src/models/line.js +156 -0
  25. package/src/{segment.js → models/segment.js} +420 -419
  26. package/src/{source.js → models/source.js} +1311 -1315
  27. package/src/player.js +2 -2
  28. package/src/{timeline-segments.js → segment-handler.js} +435 -435
  29. package/src/{timeline-sources.js → source-handler.js} +521 -514
  30. package/src/utils.js +5 -1
  31. package/src/{timeline-zoomview.js → view.js} +136 -137
  32. package/src/line.js +0 -690
  33. package/src/lines.js +0 -427
  34. /package/src/{data.js → components/data.js} +0 -0
  35. /package/src/{loader.js → components/loader.js} +0 -0
  36. /package/src/{mouse-drag-handler.js → components/mouse-drag-handler.js} +0 -0
  37. /package/src/{svgs.js → components/svgs.js} +0 -0
@@ -6,9 +6,9 @@
6
6
  * @module waveform-builder
7
7
  */
8
8
 
9
- define([
9
+ define([
10
10
  'waveform-data',
11
- './utils'
11
+ '../utils'
12
12
  ], function(
13
13
  WaveformData,
14
14
  Utils) {
@@ -1,214 +1,214 @@
1
- /**
2
- * @file
3
- *
4
- * Defines the {@link WaveformShape} class.
5
- *
6
- * @module waveform-shape
7
- */
8
-
9
- define(['./utils', 'konva'], function(Utils, Konva) {
10
- 'use strict';
11
-
12
- /**
13
- * Scales the waveform data for drawing on a canvas context.
14
- *
15
- * @param {Number} amplitude The waveform data point amplitude.
16
- * @param {Number} height The height of the waveform, in pixels.
17
- * @param {Number} scale Amplitude scaling factor.
18
- * @returns {Number} The scaled waveform data point.
19
- */
20
-
21
- function scaleY(amplitude, height, scale) {
22
- var range = 256;
23
- var offset = 128;
24
-
25
- var scaledAmplitude = (amplitude * scale + offset) * height / range;
26
-
27
- return height - Utils.clamp(height - scaledAmplitude, 0, height);
28
- }
29
-
30
- /**
31
- * Waveform shape options.
32
- *
33
- * @typedef {Object} WaveformShapeOptions
34
- * @global
35
- * @property {String} color Waveform color.
36
- * @property {WaveformOverview|WaveformZoomView} view The view object
37
- * that contains the waveform shape.
38
- * @property {Segment?} segment If given, render a waveform image
39
- * covering the segment's time range. Otherwise, render the entire
40
- * waveform duration.
41
- */
42
-
43
- /**
44
- * Creates a Konva.Shape object that renders a waveform image.
45
- *
46
- * @class
47
- * @alias WaveformShape
48
- *
49
- * @param {WaveformShapeOptions} options
50
- */
51
-
52
- function WaveformShape(options) {
53
- const shape = new Konva.Shape({
54
- fill: options.source.color
55
- });
56
-
57
- Object.assign(this, shape);
58
-
59
- this._layer = options.layer;
60
- this._view = options.view;
61
- this._source = options.source;
62
- this._height = options.height;
63
- this._url = options.url + '-scaled';
64
-
65
- this.sceneFunc(this._sceneFunc);
66
-
67
- this.hitFunc(this._waveformShapeHitFunc);
68
- }
69
-
70
- WaveformShape.prototype = Object.create(Konva.Shape.prototype);
71
-
72
- WaveformShape.prototype._sceneFunc = function(context) {
73
- var width = this._view.getWidth();
74
- var waveformData = this._layer.getLoadedData(this._url).data;
75
-
76
- var startPixel = 0, startOffset = 0;
77
-
78
- if (this._source) {
79
- startPixel = this._view.timeToPixels(this._source.mediaStartTime) + Math.max(
80
- this._view.getFrameOffset() - this._view.timeToPixels(this._source.startTime),
81
- 0
82
- );
83
-
84
- startOffset = this._view.timeToPixels(this._source.mediaStartTime);
85
- }
86
-
87
- var endPixel = width;
88
-
89
- if (this._source) {
90
- endPixel = Math.min(
91
- this._view.timeToPixels(this._source.mediaEndTime) - Math.max(
92
- this._view.timeToPixels(this._source.endTime)
93
- - this._view.getFrameOffset()
94
- - this._view.getWidth(),
95
- 0
96
- ),
97
- waveformData.length
98
- );
99
- }
100
-
101
- this._drawWaveform(
102
- context,
103
- waveformData,
104
- startPixel,
105
- startOffset,
106
- endPixel,
107
- this._height
108
- );
109
- };
110
-
111
- /**
112
- * Draws a waveform on a canvas context.
113
- *
114
- * @param {Konva.Context} context The canvas context to draw on.
115
- * @param {WaveformData} waveformData The waveform data to draw.
116
- * @param {Number} frameOffset The start position of the waveform shown
117
- * in the view, in pixels.
118
- * @param {Number} startPixels The start position of the waveform to draw,
119
- * in pixels.
120
- * @param {Number} endPixels The end position of the waveform to draw,
121
- * in pixels.
122
- * @param {Number} width The width of the waveform area, in pixels.
123
- * @param {Number} height The height of the waveform area, in pixels.
124
- */
125
-
126
- WaveformShape.prototype._drawWaveform = function(context, waveformData,
127
- startPixel, startOffset, endPixel, height) {
128
- var channels = waveformData.channels;
129
-
130
- var waveformTop = 0;
131
- var waveformHeight = Math.floor(height / channels);
132
-
133
- for (var i = 0; i < channels; i++) {
134
- if (i === channels - 1) {
135
- waveformHeight = height - (channels - 1) * waveformHeight;
136
- }
137
-
138
- this._drawChannel(
139
- context,
140
- waveformData.channel(i),
141
- startPixel,
142
- startOffset,
143
- endPixel,
144
- waveformTop,
145
- waveformHeight
146
- );
147
-
148
- waveformTop += waveformHeight;
149
- }
150
- };
151
-
152
- WaveformShape.prototype._drawChannel = function(context, channel,
153
- startPixel, startOffset, endPixel, top, height) {
154
- var x, val;
155
-
156
- var amplitudeScale = this._view.getAmplitudeScale();
157
-
158
- context.beginPath();
159
-
160
- for (x = Math.floor(startPixel); x < Math.ceil(endPixel); x++) {
161
- val = channel.min_sample(x);
162
-
163
- context.lineTo(x - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
164
- }
165
-
166
- for (x = Math.ceil(endPixel) - 1; x >= Math.floor(startPixel); x--) {
167
- val = channel.max_sample(x);
168
-
169
- context.lineTo(x - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
170
- }
171
-
172
- context.closePath();
173
-
174
- context.fillShape(this);
175
- };
176
-
177
- WaveformShape.prototype._waveformShapeHitFunc = function(context) {
178
- if (!this._source) {
179
- return;
180
- }
181
-
182
- var frameOffset = this._view.getFrameOffset();
183
- var viewWidth = this._view.getWidth();
184
-
185
- var startPixels = this._view.timeToPixels(this._source.startTime);
186
- var endPixels = this._view.timeToPixels(this._source.endTime);
187
-
188
- var offsetY = 10;
189
- var hitRectHeight = this._height;
190
-
191
- if (hitRectHeight < 0) {
192
- hitRectHeight = 0;
193
- }
194
-
195
- var hitRectLeft = startPixels - frameOffset;
196
- var hitRectWidth = endPixels - startPixels;
197
-
198
- if (hitRectLeft < 0) {
199
- hitRectWidth -= -hitRectLeft;
200
- hitRectLeft = 0;
201
- }
202
-
203
- if (hitRectLeft + hitRectWidth > viewWidth) {
204
- hitRectWidth -= hitRectLeft + hitRectWidth - viewWidth;
205
- }
206
-
207
- context.beginPath();
208
- context.rect(hitRectLeft, offsetY, hitRectWidth, hitRectHeight);
209
- context.closePath();
210
- context.fillStrokeShape(this);
211
- };
212
-
213
- return WaveformShape;
214
- });
1
+ /**
2
+ * @file
3
+ *
4
+ * Defines the {@link WaveformShape} class.
5
+ *
6
+ * @module waveform-shape
7
+ */
8
+
9
+ define(['../utils', 'konva'], function(Utils, Konva) {
10
+ 'use strict';
11
+
12
+ /**
13
+ * Scales the waveform data for drawing on a canvas context.
14
+ *
15
+ * @param {Number} amplitude The waveform data point amplitude.
16
+ * @param {Number} height The height of the waveform, in pixels.
17
+ * @param {Number} scale Amplitude scaling factor.
18
+ * @returns {Number} The scaled waveform data point.
19
+ */
20
+
21
+ function scaleY(amplitude, height, scale) {
22
+ var range = 256;
23
+ var offset = 128;
24
+
25
+ var scaledAmplitude = (amplitude * scale + offset) * height / range;
26
+
27
+ return height - Utils.clamp(height - scaledAmplitude, 0, height);
28
+ }
29
+
30
+ /**
31
+ * Waveform shape options.
32
+ *
33
+ * @typedef {Object} WaveformShapeOptions
34
+ * @global
35
+ * @property {String} color Waveform color.
36
+ * @property {WaveformOverview|WaveformZoomView} view The view object
37
+ * that contains the waveform shape.
38
+ * @property {Segment?} segment If given, render a waveform image
39
+ * covering the segment's time range. Otherwise, render the entire
40
+ * waveform duration.
41
+ */
42
+
43
+ /**
44
+ * Creates a Konva.Shape object that renders a waveform image.
45
+ *
46
+ * @class
47
+ * @alias WaveformShape
48
+ *
49
+ * @param {WaveformShapeOptions} options
50
+ */
51
+
52
+ function WaveformShape(options) {
53
+ const shape = new Konva.Shape({
54
+ fill: options.source.color
55
+ });
56
+
57
+ Object.assign(this, shape);
58
+
59
+ this._layer = options.layer;
60
+ this._view = options.view;
61
+ this._source = options.source;
62
+ this._height = options.height;
63
+ this._url = options.url + '-scaled';
64
+
65
+ this.sceneFunc(this._sceneFunc);
66
+
67
+ this.hitFunc(this._waveformShapeHitFunc);
68
+ }
69
+
70
+ WaveformShape.prototype = Object.create(Konva.Shape.prototype);
71
+
72
+ WaveformShape.prototype._sceneFunc = function(context) {
73
+ var width = this._view.getWidth();
74
+ var waveformData = this._layer.getLoadedData(this._url).data;
75
+
76
+ var startPixel = 0, startOffset = 0;
77
+
78
+ if (this._source) {
79
+ startPixel = this._view.timeToPixels(this._source.mediaStartTime) + Math.max(
80
+ this._view.getFrameOffset() - this._view.timeToPixels(this._source.startTime),
81
+ 0
82
+ );
83
+
84
+ startOffset = this._view.timeToPixels(this._source.mediaStartTime);
85
+ }
86
+
87
+ var endPixel = width;
88
+
89
+ if (this._source) {
90
+ endPixel = Math.min(
91
+ this._view.timeToPixels(this._source.mediaEndTime) - Math.max(
92
+ this._view.timeToPixels(this._source.endTime)
93
+ - this._view.getFrameOffset()
94
+ - this._view.getWidth(),
95
+ 0
96
+ ),
97
+ waveformData.length
98
+ );
99
+ }
100
+
101
+ this._drawWaveform(
102
+ context,
103
+ waveformData,
104
+ startPixel,
105
+ startOffset,
106
+ endPixel,
107
+ this._height
108
+ );
109
+ };
110
+
111
+ /**
112
+ * Draws a waveform on a canvas context.
113
+ *
114
+ * @param {Konva.Context} context The canvas context to draw on.
115
+ * @param {WaveformData} waveformData The waveform data to draw.
116
+ * @param {Number} frameOffset The start position of the waveform shown
117
+ * in the view, in pixels.
118
+ * @param {Number} startPixels The start position of the waveform to draw,
119
+ * in pixels.
120
+ * @param {Number} endPixels The end position of the waveform to draw,
121
+ * in pixels.
122
+ * @param {Number} width The width of the waveform area, in pixels.
123
+ * @param {Number} height The height of the waveform area, in pixels.
124
+ */
125
+
126
+ WaveformShape.prototype._drawWaveform = function(context, waveformData,
127
+ startPixel, startOffset, endPixel, height) {
128
+ var channels = waveformData.channels;
129
+
130
+ var waveformTop = 0;
131
+ var waveformHeight = Math.floor(height / channels);
132
+
133
+ for (var i = 0; i < channels; i++) {
134
+ if (i === channels - 1) {
135
+ waveformHeight = height - (channels - 1) * waveformHeight;
136
+ }
137
+
138
+ this._drawChannel(
139
+ context,
140
+ waveformData.channel(i),
141
+ startPixel,
142
+ startOffset,
143
+ endPixel,
144
+ waveformTop,
145
+ waveformHeight
146
+ );
147
+
148
+ waveformTop += waveformHeight;
149
+ }
150
+ };
151
+
152
+ WaveformShape.prototype._drawChannel = function(context, channel,
153
+ startPixel, startOffset, endPixel, top, height) {
154
+ var x, val;
155
+
156
+ var amplitudeScale = this._view.getAmplitudeScale();
157
+
158
+ context.beginPath();
159
+
160
+ for (x = Math.floor(startPixel); x < Math.ceil(endPixel); x++) {
161
+ val = channel.min_sample(x);
162
+
163
+ context.lineTo(x - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
164
+ }
165
+
166
+ for (x = Math.ceil(endPixel) - 1; x >= Math.floor(startPixel); x--) {
167
+ val = channel.max_sample(x);
168
+
169
+ context.lineTo(x - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
170
+ }
171
+
172
+ context.closePath();
173
+
174
+ context.fillShape(this);
175
+ };
176
+
177
+ WaveformShape.prototype._waveformShapeHitFunc = function(context) {
178
+ if (!this._source) {
179
+ return;
180
+ }
181
+
182
+ var frameOffset = this._view.getFrameOffset();
183
+ var viewWidth = this._view.getWidth();
184
+
185
+ var startPixels = this._view.timeToPixels(this._source.startTime);
186
+ var endPixels = this._view.timeToPixels(this._source.endTime);
187
+
188
+ var offsetY = 10;
189
+ var hitRectHeight = this._height;
190
+
191
+ if (hitRectHeight < 0) {
192
+ hitRectHeight = 0;
193
+ }
194
+
195
+ var hitRectLeft = startPixels - frameOffset;
196
+ var hitRectWidth = endPixels - startPixels;
197
+
198
+ if (hitRectLeft < 0) {
199
+ hitRectWidth -= -hitRectLeft;
200
+ hitRectLeft = 0;
201
+ }
202
+
203
+ if (hitRectLeft + hitRectWidth > viewWidth) {
204
+ hitRectWidth -= hitRectLeft + hitRectWidth - viewWidth;
205
+ }
206
+
207
+ context.beginPath();
208
+ context.rect(hitRectLeft, offsetY, hitRectWidth, hitRectHeight);
209
+ context.closePath();
210
+ context.fillStrokeShape(this);
211
+ };
212
+
213
+ return WaveformShape;
214
+ });
@@ -55,47 +55,47 @@ define([], function() {
55
55
 
56
56
  if (event.type === 'keydown' || event.type === 'keypress') {
57
57
  if ((event.ctrlKey || event.metaKey) && !this._ctrlCmdPressed) {
58
- this.eventEmitter.emit('keyboard.ctrlCmdDown');
58
+ this.eventEmitter.emit('handler.keyboard.ctrlCmdDown');
59
59
  this._ctrlCmdPressed = true;
60
60
  }
61
61
  switch (event.code) {
62
62
  case 'Spacebar':
63
63
  case ' ':
64
- this.eventEmitter.emit('keyboard.space');
64
+ this.eventEmitter.emit('handler.keyboard.space');
65
65
  break;
66
66
 
67
67
  case 'Tab':
68
- this.eventEmitter.emit('keyboard.tab');
68
+ this.eventEmitter.emit('handler.keyboard.tab');
69
69
  break;
70
70
  }
71
71
  }
72
72
  else if (event.type === 'keyup') {
73
73
  if (!(event.ctrlKey || event.metaKey) && this._ctrlCmdPressed) {
74
- this.eventEmitter.emit('keyboard.ctrlCmdUp');
74
+ this.eventEmitter.emit('handler.keyboard.ctrlCmdUp');
75
75
  this._ctrlCmdPressed = false;
76
76
  }
77
77
  switch (event.code) {
78
78
  case 'ArrowLeft':
79
79
  if (event.shiftKey) {
80
- this.eventEmitter.emit('keyboard.shift_left');
80
+ this.eventEmitter.emit('handler.keyboard.shiftleft');
81
81
  }
82
82
  else {
83
- this.eventEmitter.emit('keyboard.left');
83
+ this.eventEmitter.emit('handler.keyboard.left');
84
84
  }
85
85
  break;
86
86
 
87
87
  case 'ArrowRight':
88
88
  if (event.shiftKey) {
89
- this.eventEmitter.emit('keyboard.shift_right');
89
+ this.eventEmitter.emit('handler.keyboard.shiftright');
90
90
  }
91
91
  else {
92
- this.eventEmitter.emit('keyboard.right');
92
+ this.eventEmitter.emit('handler.keyboard.right');
93
93
  }
94
94
  break;
95
95
 
96
96
  case 'Delete':
97
97
  case 'Backspace':
98
- this.eventEmitter.emit('keyboard.delete');
98
+ this.eventEmitter.emit('handler.keyboard.delete');
99
99
  break;
100
100
  }
101
101
  }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Defines the {@link LineHandler} class.
5
+ *
6
+ * @module line-handler
7
+ */
8
+
9
+ define([
10
+ './models/line',
11
+ './utils'
12
+ ], function(Line, Utils) {
13
+ 'use strict';
14
+
15
+ /**
16
+ * Line parameters.
17
+ *
18
+ * @typedef {Object} LineOptions
19
+ * @global
20
+ * @param {String} id A unique identifier for the line.
21
+ * @param {Number} position Position of the line on the timeline.
22
+ * @param {String} indicatorType Type of the line indicator.
23
+ * @param {String} indicatorText Text to display above the line indicator.
24
+ */
25
+
26
+ /**
27
+ * Handles all functionality related to the adding, removing and manipulation
28
+ * of lines.
29
+ *
30
+ * @class
31
+ * @alias LineHandler
32
+ *
33
+ * @param {Peaks} peaks The parent Peaks object.
34
+ */
35
+
36
+ function LineHandler(peaks) {
37
+ this._peaks = peaks;
38
+ this._lines = [];
39
+ this._linesById = {};
40
+ }
41
+
42
+ /**
43
+ * Adds a new line object.
44
+ *
45
+ * @private
46
+ * @param {Line} line
47
+ */
48
+
49
+ LineHandler.prototype._addLine = function(line) {
50
+ this._linesById[line.id] = line;
51
+ };
52
+
53
+ /**
54
+ * Creates a new line object.
55
+ *
56
+ * @private
57
+ * @param {LineOptions} options
58
+ * @return {Line}
59
+ */
60
+
61
+ LineHandler.prototype._createLine = function(options) {
62
+ if (!Utils.isObject(options)) {
63
+ throw new TypeError('peaks.lines.add(): expected a Line object parameter');
64
+ }
65
+
66
+ var line = new Line(
67
+ this._peaks,
68
+ Utils.isNullOrUndefined(options.id) ? Utils.createUuidv4() : options.id,
69
+ options.position,
70
+ options.indicatorType,
71
+ options.indicatorText
72
+ );
73
+
74
+ return line;
75
+ };
76
+
77
+ /**
78
+ * Returns all lines.
79
+ *
80
+ * @returns {Array<Line>}
81
+ */
82
+
83
+ LineHandler.prototype.getLines = function() {
84
+ return Object.values(this._linesById);
85
+ };
86
+
87
+ /**
88
+ * Returns all lines, indexed by their id.
89
+ *
90
+ * @returns {Object<String, Line>}
91
+ */
92
+ LineHandler.prototype.getLinesById = function() {
93
+ return this._linesById;
94
+ };
95
+
96
+ /**
97
+ * Returns the line with the given id, or <code>null</code> if not found.
98
+ *
99
+ * @param {String} id
100
+ * @returns {Line|null}
101
+ */
102
+
103
+ LineHandler.prototype.getLine = function(id) {
104
+ return this._linesById[id] || null;
105
+ };
106
+
107
+ /**
108
+ * Adds one or more lines to the timeline.
109
+ *
110
+ * @param {LineOptions|Array<LineOptions>} lineOrLines
111
+ */
112
+
113
+ LineHandler.prototype.add = function(/* lineOrLines */) {
114
+ var lines = Array.isArray(arguments[0]) ?
115
+ arguments[0] :
116
+ Array.prototype.slice.call(arguments);
117
+
118
+ lines = lines.map(function(lineOptions) {
119
+ var line = this._createLine(lineOptions);
120
+
121
+ if (Utils.objectHasProperty(this._linesById, line.id)) {
122
+ var existingLine = this._linesById[line.id];
123
+
124
+ throw new Error('peaks.lines.add(): duplicate id. \n\n Line I = ' +
125
+ JSON.stringify(
126
+ existingLine.toSerializable()
127
+ ) + ' Line II = ' +
128
+ JSON.stringify(
129
+ line.toSerializable()
130
+ )
131
+ );
132
+ }
133
+
134
+ return line;
135
+ }.bind(this));
136
+
137
+ lines.forEach(function(line) {
138
+ this._addLine(line);
139
+ }.bind(this));
140
+
141
+ this._peaks.emit('handler.lines.add', lines);
142
+
143
+ return lines;
144
+ };
145
+
146
+ /**
147
+ * Removes any lines with the given id(s).
148
+ *
149
+ * @param {String} id
150
+ * @param {Array<String>} ids
151
+ * @returns {Array<Line>} The removed {@link Line} objects.
152
+ */
153
+
154
+ LineHandler.prototype.removeById = function(/* lineIdOrIds */) {
155
+ var lineIds = Array.isArray(arguments[0]) ?
156
+ arguments[0] :
157
+ Array.prototype.slice.call(arguments);
158
+
159
+ const lines = lineIds.map(function(lineId) {
160
+ const line = this._linesById[lineId];
161
+
162
+ if (!line) {
163
+ throw new Error('peaks.lines.removeById(): line with id ' + lineId + ' not found');
164
+ }
165
+
166
+ return line;
167
+ }.bind(this));
168
+
169
+ lines.forEach(function(line) {
170
+ delete this._linesById[line.id];
171
+ }.bind(this));
172
+
173
+ this._peaks.emit('handler.lines.remove', lines);
174
+
175
+ return lines;
176
+ };
177
+
178
+ return LineHandler;
179
+ });