@jdeighan/coffee-utils 8.0.2 → 8.0.5

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@jdeighan/coffee-utils",
3
3
  "type": "module",
4
- "version": "8.0.2",
4
+ "version": "8.0.5",
5
5
  "description": "A set of utility functions for CoffeeScript",
6
6
  "main": "coffee_utils.js",
7
7
  "exports": {
@@ -19,6 +19,8 @@
19
19
  "./store": "./src/DataStores.js",
20
20
  "./taml": "./src/taml.js",
21
21
  "./placeholders": "./src/placeholders.js",
22
+ "./section": "./src/Section.js",
23
+ "./sectionmap": "./src/SectionMap.js",
22
24
  "./package.json": "./package.json"
23
25
  },
24
26
  "engines": {
@@ -53,6 +55,6 @@
53
55
  "svelte": "^3.48.0"
54
56
  },
55
57
  "devDependencies": {
56
- "@jdeighan/unit-tester": "^2.0.8"
58
+ "@jdeighan/unit-tester": "^2.0.9"
57
59
  }
58
60
  }
@@ -0,0 +1,69 @@
1
+ # Section.coffee
2
+
3
+ import {
4
+ assert, pass, undef, defined, croak, isArray,
5
+ } from '@jdeighan/coffee-utils'
6
+ import {arrayToBlock} from '@jdeighan/coffee-utils/block'
7
+ import {indented} from '@jdeighan/coffee-utils/indent'
8
+
9
+ # ---------------------------------------------------------------------------
10
+
11
+ export class Section
12
+
13
+ constructor: (@name) ->
14
+
15
+ @lLines = []
16
+
17
+ # ..........................................................
18
+
19
+ length: () ->
20
+
21
+ return @lLines.length
22
+
23
+ # ..........................................................
24
+
25
+ indent: (level=1) ->
26
+
27
+ lNewLines = for line in @lLines
28
+ indented(line, level)
29
+ @lLines = lNewLines
30
+ return
31
+
32
+ # ..........................................................
33
+
34
+ isEmpty: () ->
35
+
36
+ return (@lLines.length == 0)
37
+
38
+ # ..........................................................
39
+
40
+ nonEmpty: () ->
41
+
42
+ return (@lLines.length > 0)
43
+
44
+ # ..........................................................
45
+
46
+ add: (data) ->
47
+
48
+ if isArray(data)
49
+ for line in data
50
+ @lLines.push line
51
+ else
52
+ @lLines.push data
53
+ return
54
+
55
+ # ..........................................................
56
+
57
+ prepend: (data) ->
58
+
59
+ if isArray(data)
60
+ @lLines = [data..., @lLines...]
61
+ else
62
+ @lLines = [data, @lLines...]
63
+ return
64
+
65
+ # ..........................................................
66
+
67
+ getBlock: () ->
68
+
69
+ return arrayToBlock(@lLines)
package/src/Section.js ADDED
@@ -0,0 +1,85 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ // Section.coffee
3
+ import {
4
+ assert,
5
+ pass,
6
+ undef,
7
+ defined,
8
+ croak,
9
+ isArray
10
+ } from '@jdeighan/coffee-utils';
11
+
12
+ import {
13
+ arrayToBlock
14
+ } from '@jdeighan/coffee-utils/block';
15
+
16
+ import {
17
+ indented
18
+ } from '@jdeighan/coffee-utils/indent';
19
+
20
+ // ---------------------------------------------------------------------------
21
+ export var Section = class Section {
22
+ constructor(name) {
23
+ this.name = name;
24
+ this.lLines = [];
25
+ }
26
+
27
+ // ..........................................................
28
+ length() {
29
+ return this.lLines.length;
30
+ }
31
+
32
+ // ..........................................................
33
+ indent(level = 1) {
34
+ var lNewLines, line;
35
+ lNewLines = (function() {
36
+ var i, len, ref, results;
37
+ ref = this.lLines;
38
+ results = [];
39
+ for (i = 0, len = ref.length; i < len; i++) {
40
+ line = ref[i];
41
+ results.push(indented(line, level));
42
+ }
43
+ return results;
44
+ }).call(this);
45
+ this.lLines = lNewLines;
46
+ }
47
+
48
+ // ..........................................................
49
+ isEmpty() {
50
+ return this.lLines.length === 0;
51
+ }
52
+
53
+ // ..........................................................
54
+ nonEmpty() {
55
+ return this.lLines.length > 0;
56
+ }
57
+
58
+ // ..........................................................
59
+ add(data) {
60
+ var i, len, line;
61
+ if (isArray(data)) {
62
+ for (i = 0, len = data.length; i < len; i++) {
63
+ line = data[i];
64
+ this.lLines.push(line);
65
+ }
66
+ } else {
67
+ this.lLines.push(data);
68
+ }
69
+ }
70
+
71
+ // ..........................................................
72
+ prepend(data) {
73
+ if (isArray(data)) {
74
+ this.lLines = [...data, ...this.lLines];
75
+ } else {
76
+ this.lLines = [data, ...this.lLines];
77
+ }
78
+ }
79
+
80
+ // ..........................................................
81
+ getBlock() {
82
+ return arrayToBlock(this.lLines);
83
+ }
84
+
85
+ };
@@ -0,0 +1,221 @@
1
+ # SectionMap.coffee
2
+
3
+ import {
4
+ assert, pass, undef, defined, croak, OL, isEmpty, nonEmpty,
5
+ isString, isHash, isArray, isUniqueTree, isNonEmptyString,
6
+ } from '@jdeighan/coffee-utils'
7
+ import {arrayToBlock} from '@jdeighan/coffee-utils/block'
8
+ import {indented} from '@jdeighan/coffee-utils/indent'
9
+ import {debug} from '@jdeighan/coffee-utils/debug'
10
+ import {Section} from '@jdeighan/coffee-utils/section'
11
+
12
+ # ---------------------------------------------------------------------------
13
+
14
+ isSectionName = (name) ->
15
+
16
+ return isString(name) && name.match(/^[a-z][a-z0-9]*/)
17
+
18
+ # ---------------------------------------------------------------------------
19
+
20
+ isSetName = (name) ->
21
+
22
+ return isString(name) && name.match(/^[A-Z][a-z0-9]*/)
23
+
24
+ # ---------------------------------------------------------------------------
25
+
26
+ export class SectionMap
27
+
28
+ constructor: (lSectionTree) ->
29
+ # --- lSectionTree is a tree of section names
30
+
31
+ debug "enter SectionMap()", lSectionTree
32
+ @lSectionTree = lSectionTree # a tree of section names
33
+ @hSets = {}
34
+ @hSections = {}
35
+ @addSections lSectionTree
36
+ debug "return from SectionMap()", @hSections
37
+
38
+ # ..........................................................
39
+
40
+ addSections: (desc) ->
41
+ # --- returns a flat array of sections that were added
42
+
43
+ if isString(desc)
44
+ assert nonEmpty(desc), "empty section name"
45
+ assert isSectionName(desc), "bad section name #{OL(desc)}"
46
+ assert (@hSections[desc] == undef), "duplicate section #{OL(desc)}"
47
+ @hSections[desc] = new Section(desc)
48
+ return [desc]
49
+ else
50
+ assert isArray(desc), "not an array or string #{OL(desc)}"
51
+ name = undef
52
+ lParts = []
53
+ for item,i in desc
54
+ if (i==0) && isSetName(item)
55
+ name = item
56
+ else
57
+ lAdded = @addSections item
58
+ for item in lAdded
59
+ lParts.push item
60
+ if defined(name)
61
+ @addSet name, lParts
62
+ return lParts
63
+ return
64
+
65
+ # ..........................................................
66
+
67
+ addSet: (name, lSectionTree) ->
68
+
69
+ debug "enter addSet()", name, lSectionTree
70
+
71
+ # --- check the name
72
+ assert isSetName(name), "not a valid set name #{OL(name)}"
73
+
74
+ # --- check lSectionTree
75
+ assert isArray(lSectionTree), "arg 2 not an array"
76
+ for secName in lSectionTree
77
+ assert isNonEmptyString(secName),
78
+ "not a non-empty string #{OL(secName)}"
79
+ assert defined(@hSections[secName]),
80
+ "not a section name #{OL(secName)}"
81
+
82
+ @hSets[name] = lSectionTree
83
+ debug 'hSets', @hSets
84
+ debug "return from addSet()"
85
+ return
86
+
87
+ # ..........................................................
88
+ # --- sections returned in depth-first order from section tree
89
+ # Set names are simply skipped
90
+ # yields: [<level>, <section>]
91
+
92
+ allSections: (desc=undef, level=0) ->
93
+
94
+ debug "enter allSections()", desc, level
95
+ if (desc == undef)
96
+ desc = @lSectionTree
97
+ if isArray(desc)
98
+ for item in desc
99
+ if isSectionName(item)
100
+ result = [level, @section(item)]
101
+ debug 'yield', result
102
+ yield result
103
+ else if isSetName(item)
104
+ pass
105
+ else
106
+ assert isArray(item), "not an array #{OL(item)}"
107
+ yield from @allSections(item, level+1)
108
+ else if isSectionName(desc)
109
+ result = [level, @section(desc)]
110
+ debug 'yield', result
111
+ yield result
112
+ else if isSetName(desc)
113
+ lTree = @hSets[desc]
114
+ assert defined(lTree), "Not a Set: #{OL(desc)}"
115
+ yield from @allSections(lTree, level)
116
+ else
117
+ croak "Bad item: #{OL(desc)}"
118
+ debug "return from allSections()"
119
+ return
120
+
121
+ # ..........................................................
122
+
123
+ getBlock: () ->
124
+
125
+ debug "enter getBlock()"
126
+ lParts = []
127
+ for [_, sect] from @allSections()
128
+ if sect.nonEmpty()
129
+ lParts.push sect.getBlock()
130
+ debug 'lParts', lParts
131
+ result = arrayToBlock(lParts)
132
+ debug "return from getBlock()", result
133
+ return result
134
+
135
+ # ..........................................................
136
+
137
+ section: (name) ->
138
+
139
+ sect = @hSections[name]
140
+ assert defined(sect), "No section named #{OL(name)}"
141
+ return sect
142
+
143
+ # ..........................................................
144
+
145
+ firstSection: (name) ->
146
+
147
+ assert isSetName(name), "bad set name #{OL(name)}"
148
+ lSectionTree = @hSets[name]
149
+ assert defined(lSectionTree), "no such set #{OL(name)}"
150
+ assert nonEmpty(lSectionTree), "empty section #{OL(name)}"
151
+ return @section(lSectionTree[0])
152
+
153
+ # ..........................................................
154
+
155
+ lastSection: (name) ->
156
+
157
+ assert isSetName(name), "bad set name #{OL(name)}"
158
+ lSectionTree = @hSets[name]
159
+ assert defined(lSectionTree), "no such set #{OL(name)}"
160
+ assert nonEmpty(lSectionTree), "empty section #{OL(name)}"
161
+ return @section(lSectionTree[lSectionTree.length - 1])
162
+
163
+ # ..........................................................
164
+
165
+ length: (desc=undef) ->
166
+
167
+ result = 0
168
+ for [_, sect] from @allSections(desc)
169
+ result += sect.length()
170
+ return result
171
+
172
+ # ..........................................................
173
+
174
+ isEmpty: (desc=undef) ->
175
+
176
+ return (@length(desc) == 0)
177
+
178
+ # ..........................................................
179
+
180
+ nonEmpty: (desc=undef) ->
181
+
182
+ return (@length(desc) > 0)
183
+
184
+ # ..........................................................
185
+
186
+ indent: (desc=undef, level=1) ->
187
+
188
+ for [_, sect] from @allSections(desc)
189
+ sect.indent(level)
190
+ return
191
+
192
+ # ..........................................................
193
+
194
+ enclose: (name, pre, post) ->
195
+
196
+ if isSectionName(name)
197
+ sect = @section(name)
198
+ if sect.nonEmpty()
199
+ sect.indent()
200
+ sect.prepend(pre)
201
+ sect.add(post)
202
+ else if isSetName(name)
203
+ if @nonEmpty(name)
204
+ @indent(name)
205
+ @firstSection(name).prepend(pre)
206
+ @lastSection(name).add(post)
207
+ else
208
+ croak "Bad name param #{OL(name)}"
209
+ return
210
+
211
+ # ..........................................................
212
+
213
+ getShape: () ->
214
+
215
+ debug "enter getShape()"
216
+ lParts = []
217
+ for [level, sect] from @allSections()
218
+ lParts.push indented(sect.name, level)
219
+ result = arrayToBlock(lParts)
220
+ debug "return from getShape()", result
221
+ return result
@@ -0,0 +1,262 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ // SectionMap.coffee
3
+ var isSectionName, isSetName;
4
+
5
+ import {
6
+ assert,
7
+ pass,
8
+ undef,
9
+ defined,
10
+ croak,
11
+ OL,
12
+ isEmpty,
13
+ nonEmpty,
14
+ isString,
15
+ isHash,
16
+ isArray,
17
+ isUniqueTree,
18
+ isNonEmptyString
19
+ } from '@jdeighan/coffee-utils';
20
+
21
+ import {
22
+ arrayToBlock
23
+ } from '@jdeighan/coffee-utils/block';
24
+
25
+ import {
26
+ indented
27
+ } from '@jdeighan/coffee-utils/indent';
28
+
29
+ import {
30
+ debug
31
+ } from '@jdeighan/coffee-utils/debug';
32
+
33
+ import {
34
+ Section
35
+ } from '@jdeighan/coffee-utils/section';
36
+
37
+ // ---------------------------------------------------------------------------
38
+ isSectionName = function(name) {
39
+ return isString(name) && name.match(/^[a-z][a-z0-9]*/);
40
+ };
41
+
42
+ // ---------------------------------------------------------------------------
43
+ isSetName = function(name) {
44
+ return isString(name) && name.match(/^[A-Z][a-z0-9]*/);
45
+ };
46
+
47
+ // ---------------------------------------------------------------------------
48
+ export var SectionMap = class SectionMap {
49
+ constructor(lSectionTree) {
50
+ // --- lSectionTree is a tree of section names
51
+ debug("enter SectionMap()", lSectionTree);
52
+ this.lSectionTree = lSectionTree; // a tree of section names
53
+ this.hSets = {};
54
+ this.hSections = {};
55
+ this.addSections(lSectionTree);
56
+ debug("return from SectionMap()", this.hSections);
57
+ }
58
+
59
+ // ..........................................................
60
+ addSections(desc) {
61
+ var i, item, j, k, lAdded, lParts, len, len1, name;
62
+ // --- returns a flat array of sections that were added
63
+ if (isString(desc)) {
64
+ assert(nonEmpty(desc), "empty section name");
65
+ assert(isSectionName(desc), `bad section name ${OL(desc)}`);
66
+ assert(this.hSections[desc] === undef, `duplicate section ${OL(desc)}`);
67
+ this.hSections[desc] = new Section(desc);
68
+ return [desc];
69
+ } else {
70
+ assert(isArray(desc), `not an array or string ${OL(desc)}`);
71
+ name = undef;
72
+ lParts = [];
73
+ for (i = j = 0, len = desc.length; j < len; i = ++j) {
74
+ item = desc[i];
75
+ if ((i === 0) && isSetName(item)) {
76
+ name = item;
77
+ } else {
78
+ lAdded = this.addSections(item);
79
+ for (k = 0, len1 = lAdded.length; k < len1; k++) {
80
+ item = lAdded[k];
81
+ lParts.push(item);
82
+ }
83
+ }
84
+ }
85
+ if (defined(name)) {
86
+ this.addSet(name, lParts);
87
+ }
88
+ return lParts;
89
+ }
90
+ }
91
+
92
+ // ..........................................................
93
+ addSet(name, lSectionTree) {
94
+ var j, len, secName;
95
+ debug("enter addSet()", name, lSectionTree);
96
+ // --- check the name
97
+ assert(isSetName(name), `not a valid set name ${OL(name)}`);
98
+ // --- check lSectionTree
99
+ assert(isArray(lSectionTree), "arg 2 not an array");
100
+ for (j = 0, len = lSectionTree.length; j < len; j++) {
101
+ secName = lSectionTree[j];
102
+ assert(isNonEmptyString(secName), `not a non-empty string ${OL(secName)}`);
103
+ assert(defined(this.hSections[secName]), `not a section name ${OL(secName)}`);
104
+ }
105
+ this.hSets[name] = lSectionTree;
106
+ debug('hSets', this.hSets);
107
+ debug("return from addSet()");
108
+ }
109
+
110
+ // ..........................................................
111
+ // --- sections returned in depth-first order from section tree
112
+ // Set names are simply skipped
113
+ // yields: [<level>, <section>]
114
+ * allSections(desc = undef, level = 0) {
115
+ var item, j, lTree, len, result;
116
+ debug("enter allSections()", desc, level);
117
+ if (desc === undef) {
118
+ desc = this.lSectionTree;
119
+ }
120
+ if (isArray(desc)) {
121
+ for (j = 0, len = desc.length; j < len; j++) {
122
+ item = desc[j];
123
+ if (isSectionName(item)) {
124
+ result = [level, this.section(item)];
125
+ debug('yield', result);
126
+ yield result;
127
+ } else if (isSetName(item)) {
128
+ pass;
129
+ } else {
130
+ assert(isArray(item), `not an array ${OL(item)}`);
131
+ yield* this.allSections(item, level + 1);
132
+ }
133
+ }
134
+ } else if (isSectionName(desc)) {
135
+ result = [level, this.section(desc)];
136
+ debug('yield', result);
137
+ yield result;
138
+ } else if (isSetName(desc)) {
139
+ lTree = this.hSets[desc];
140
+ assert(defined(lTree), `Not a Set: ${OL(desc)}`);
141
+ yield* this.allSections(lTree, level);
142
+ } else {
143
+ croak(`Bad item: ${OL(desc)}`);
144
+ }
145
+ debug("return from allSections()");
146
+ }
147
+
148
+ // ..........................................................
149
+ getBlock() {
150
+ var _, lParts, ref, result, sect, x;
151
+ debug("enter getBlock()");
152
+ lParts = [];
153
+ ref = this.allSections();
154
+ for (x of ref) {
155
+ [_, sect] = x;
156
+ if (sect.nonEmpty()) {
157
+ lParts.push(sect.getBlock());
158
+ }
159
+ }
160
+ debug('lParts', lParts);
161
+ result = arrayToBlock(lParts);
162
+ debug("return from getBlock()", result);
163
+ return result;
164
+ }
165
+
166
+ // ..........................................................
167
+ section(name) {
168
+ var sect;
169
+ sect = this.hSections[name];
170
+ assert(defined(sect), `No section named ${OL(name)}`);
171
+ return sect;
172
+ }
173
+
174
+ // ..........................................................
175
+ firstSection(name) {
176
+ var lSectionTree;
177
+ assert(isSetName(name), `bad set name ${OL(name)}`);
178
+ lSectionTree = this.hSets[name];
179
+ assert(defined(lSectionTree), `no such set ${OL(name)}`);
180
+ assert(nonEmpty(lSectionTree), `empty section ${OL(name)}`);
181
+ return this.section(lSectionTree[0]);
182
+ }
183
+
184
+ // ..........................................................
185
+ lastSection(name) {
186
+ var lSectionTree;
187
+ assert(isSetName(name), `bad set name ${OL(name)}`);
188
+ lSectionTree = this.hSets[name];
189
+ assert(defined(lSectionTree), `no such set ${OL(name)}`);
190
+ assert(nonEmpty(lSectionTree), `empty section ${OL(name)}`);
191
+ return this.section(lSectionTree[lSectionTree.length - 1]);
192
+ }
193
+
194
+ // ..........................................................
195
+ length(desc = undef) {
196
+ var _, ref, result, sect, x;
197
+ result = 0;
198
+ ref = this.allSections(desc);
199
+ for (x of ref) {
200
+ [_, sect] = x;
201
+ result += sect.length();
202
+ }
203
+ return result;
204
+ }
205
+
206
+ // ..........................................................
207
+ isEmpty(desc = undef) {
208
+ return this.length(desc) === 0;
209
+ }
210
+
211
+ // ..........................................................
212
+ nonEmpty(desc = undef) {
213
+ return this.length(desc) > 0;
214
+ }
215
+
216
+ // ..........................................................
217
+ indent(desc = undef, level = 1) {
218
+ var _, ref, sect, x;
219
+ ref = this.allSections(desc);
220
+ for (x of ref) {
221
+ [_, sect] = x;
222
+ sect.indent(level);
223
+ }
224
+ }
225
+
226
+ // ..........................................................
227
+ enclose(name, pre, post) {
228
+ var sect;
229
+ if (isSectionName(name)) {
230
+ sect = this.section(name);
231
+ if (sect.nonEmpty()) {
232
+ sect.indent();
233
+ sect.prepend(pre);
234
+ sect.add(post);
235
+ }
236
+ } else if (isSetName(name)) {
237
+ if (this.nonEmpty(name)) {
238
+ this.indent(name);
239
+ this.firstSection(name).prepend(pre);
240
+ this.lastSection(name).add(post);
241
+ }
242
+ } else {
243
+ croak(`Bad name param ${OL(name)}`);
244
+ }
245
+ }
246
+
247
+ // ..........................................................
248
+ getShape() {
249
+ var lParts, level, ref, result, sect, x;
250
+ debug("enter getShape()");
251
+ lParts = [];
252
+ ref = this.allSections();
253
+ for (x of ref) {
254
+ [level, sect] = x;
255
+ lParts.push(indented(sect.name, level));
256
+ }
257
+ result = arrayToBlock(lParts);
258
+ debug("return from getShape()", result);
259
+ return result;
260
+ }
261
+
262
+ };
@@ -30,19 +30,31 @@ export blockToArray = (block) ->
30
30
  export arrayToBlock = (lLines) ->
31
31
 
32
32
  if (lLines == undef)
33
- return ''
33
+ return undef
34
34
  assert isArray(lLines), "lLines is not an array"
35
35
  lLines = lLines.filter((line) => defined(line));
36
36
  if lLines.length == 0
37
- return ''
37
+ return undef
38
38
  else
39
39
  return rtrim(lLines.join('\n'))
40
40
 
41
41
  # ---------------------------------------------------------------------------
42
42
 
43
+ export splitBlock = (block) ->
44
+
45
+ assert isString(block), "not a string"
46
+ pos = block.indexOf('\n')
47
+ if (pos == -1)
48
+ return [block, undef]
49
+ else
50
+ return [block.substring(0, pos), block.substring(pos+1)]
51
+
52
+
53
+ # ---------------------------------------------------------------------------
54
+
43
55
  export firstLine = (block) ->
44
56
 
45
- assert isString(block), "firstLine(): string expected"
57
+ assert isString(block), "not a string"
46
58
  pos = block.indexOf('\n')
47
59
  if (pos == -1)
48
60
  return block
@@ -53,10 +65,10 @@ export firstLine = (block) ->
53
65
 
54
66
  export remainingLines = (block) ->
55
67
 
56
- assert isString(block), "remainingLines(): string expected"
68
+ assert isString(block), "not a string"
57
69
  pos = block.indexOf('\n')
58
70
  if (pos == -1)
59
- return ''
71
+ return undef
60
72
  else
61
73
  return block.substring(pos+1)
62
74
 
@@ -38,23 +38,35 @@ export var blockToArray = function(block) {
38
38
  // arrayToBlock - block will have no trailing whitespace
39
39
  export var arrayToBlock = function(lLines) {
40
40
  if (lLines === undef) {
41
- return '';
41
+ return undef;
42
42
  }
43
43
  assert(isArray(lLines), "lLines is not an array");
44
44
  lLines = lLines.filter((line) => {
45
45
  return defined(line);
46
46
  });
47
47
  if (lLines.length === 0) {
48
- return '';
48
+ return undef;
49
49
  } else {
50
50
  return rtrim(lLines.join('\n'));
51
51
  }
52
52
  };
53
53
 
54
+ // ---------------------------------------------------------------------------
55
+ export var splitBlock = function(block) {
56
+ var pos;
57
+ assert(isString(block), "not a string");
58
+ pos = block.indexOf('\n');
59
+ if (pos === -1) {
60
+ return [block, undef];
61
+ } else {
62
+ return [block.substring(0, pos), block.substring(pos + 1)];
63
+ }
64
+ };
65
+
54
66
  // ---------------------------------------------------------------------------
55
67
  export var firstLine = function(block) {
56
68
  var pos;
57
- assert(isString(block), "firstLine(): string expected");
69
+ assert(isString(block), "not a string");
58
70
  pos = block.indexOf('\n');
59
71
  if (pos === -1) {
60
72
  return block;
@@ -66,10 +78,10 @@ export var firstLine = function(block) {
66
78
  // ---------------------------------------------------------------------------
67
79
  export var remainingLines = function(block) {
68
80
  var pos;
69
- assert(isString(block), "remainingLines(): string expected");
81
+ assert(isString(block), "not a string");
70
82
  pos = block.indexOf('\n');
71
83
  if (pos === -1) {
72
- return '';
84
+ return undef;
73
85
  } else {
74
86
  return block.substring(pos + 1);
75
87
  }