@jdeighan/coffee-utils 10.0.2 → 10.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": "10.0.2",
4
+ "version": "10.0.5",
5
5
  "description": "A set of utility functions for CoffeeScript",
6
6
  "main": "coffee_utils.js",
7
7
  "exports": {
@@ -12,6 +12,7 @@
12
12
  "./log": "./src/log_utils.js",
13
13
  "./block": "./src/block_utils.js",
14
14
  "./indent": "./src/indent_utils.js",
15
+ "./html": "./src/html_utils.js",
15
16
  "./stack": "./src/call_stack.js",
16
17
  "./debug": "./src/debug_utils.js",
17
18
  "./arrow": "./src/arrow.js",
@@ -10,6 +10,7 @@ import {arrayToBlock} from '@jdeighan/coffee-utils/block'
10
10
  import {indented} from '@jdeighan/coffee-utils/indent'
11
11
  import {LOG} from '@jdeighan/coffee-utils/log'
12
12
  import {debug} from '@jdeighan/coffee-utils/debug'
13
+ import {isTAML, taml} from '@jdeighan/coffee-utils/taml'
13
14
  import {Section} from '@jdeighan/coffee-utils/section'
14
15
 
15
16
  # ---------------------------------------------------------------------------
@@ -32,6 +33,10 @@ export class SectionMap
32
33
  # --- lSectionTree is a tree of section/set names
33
34
 
34
35
  debug "enter SectionMap()", @lSectionTree
36
+
37
+ if isTAML(@lSectionTree)
38
+ @SectionTree = taml(@lSectionTree)
39
+
35
40
  assert isArray(@lSectionTree), "not an array"
36
41
 
37
42
  # --- keys are section names, values are Section objects
@@ -97,33 +102,41 @@ export class SectionMap
97
102
  hReplacers = desc
98
103
  desc = @lSectionTree
99
104
  else if notdefined(desc)
105
+ debug "desc is entire tree"
100
106
  desc = @lSectionTree
101
107
 
102
108
  if isArray(desc)
109
+ debug "item is an array"
103
110
  lBlocks = []
104
111
  setName = undef
105
112
  for item in desc
106
113
  subBlock = undef
107
- if isArray(item)
108
- subBlock = @getBlock(item, hReplacers)
109
- else if isSectionName(item)
110
- subBlock = @getBlock(item, hReplacers)
111
- else if isSetName(item)
114
+ if isSetName(item)
115
+ debug "set name is #{item}"
112
116
  setName = item
113
- else if isString(item)
114
- subBlock = item # a literal string
117
+ else if isSectionName(item) || isArray(item)
118
+ subBlock = @getBlock(item, hReplacers)
119
+ if defined(subBlock)
120
+ debug "add subBlock", subBlock
121
+ lBlocks.push subBlock
122
+ else
123
+ debug "subBlock is undef"
124
+ else if isString(item) && nonEmpty(item)
125
+ debug "add string", item
126
+ lBlocks.push item
115
127
  else
116
128
  croak "Bad item: #{OL(item)}"
117
- if defined(subBlock)
118
- lBlocks.push subBlock
129
+
119
130
  block = arrayToBlock(lBlocks)
131
+ debug "block is", block
120
132
  if defined(setName)
121
133
  if defined(proc = hReplacers[setName])
122
- debug "REPLACE #{setName}"
123
134
  block = proc(block)
135
+ debug "REPLACE #{setName} with", block
124
136
  else
125
137
  debug "NO REPLACER for #{setName}"
126
138
  else if isSectionName(desc)
139
+ debug "item is a section name"
127
140
  block = @section(desc).getBlock()
128
141
  if defined(proc = hReplacers[desc])
129
142
  debug "REPLACE #{desc}"
@@ -131,7 +144,7 @@ export class SectionMap
131
144
  else
132
145
  debug "NO REPLACER for #{desc}"
133
146
  else if isSetName(desc)
134
- # --- pass array to getBlock()
147
+ debug "item is a set name"
135
148
  block = @getBlock(@hSets[desc], hReplacers)
136
149
  else
137
150
  croak "Bad 1st arg: #{OL(desc)}"
package/src/SectionMap.js CHANGED
@@ -40,6 +40,11 @@ import {
40
40
  debug
41
41
  } from '@jdeighan/coffee-utils/debug';
42
42
 
43
+ import {
44
+ isTAML,
45
+ taml
46
+ } from '@jdeighan/coffee-utils/taml';
47
+
43
48
  import {
44
49
  Section
45
50
  } from '@jdeighan/coffee-utils/section';
@@ -60,6 +65,9 @@ export var SectionMap = class SectionMap {
60
65
  this.lSectionTree = lSectionTree;
61
66
  // --- lSectionTree is a tree of section/set names
62
67
  debug("enter SectionMap()", this.lSectionTree);
68
+ if (isTAML(this.lSectionTree)) {
69
+ this.SectionTree = taml(this.lSectionTree);
70
+ }
63
71
  assert(isArray(this.lSectionTree), "not an array");
64
72
  // --- keys are section names, values are Section objects
65
73
  this.hSections = {};
@@ -126,39 +134,46 @@ export var SectionMap = class SectionMap {
126
134
  hReplacers = desc;
127
135
  desc = this.lSectionTree;
128
136
  } else if (notdefined(desc)) {
137
+ debug("desc is entire tree");
129
138
  desc = this.lSectionTree;
130
139
  }
131
140
  if (isArray(desc)) {
141
+ debug("item is an array");
132
142
  lBlocks = [];
133
143
  setName = undef;
134
144
  for (i = 0, len = desc.length; i < len; i++) {
135
145
  item = desc[i];
136
146
  subBlock = undef;
137
- if (isArray(item)) {
138
- subBlock = this.getBlock(item, hReplacers);
139
- } else if (isSectionName(item)) {
140
- subBlock = this.getBlock(item, hReplacers);
141
- } else if (isSetName(item)) {
147
+ if (isSetName(item)) {
148
+ debug(`set name is ${item}`);
142
149
  setName = item;
143
- } else if (isString(item)) {
144
- subBlock = item; // a literal string
150
+ } else if (isSectionName(item) || isArray(item)) {
151
+ subBlock = this.getBlock(item, hReplacers);
152
+ if (defined(subBlock)) {
153
+ debug("add subBlock", subBlock);
154
+ lBlocks.push(subBlock);
155
+ } else {
156
+ debug("subBlock is undef");
157
+ }
158
+ } else if (isString(item) && nonEmpty(item)) {
159
+ debug("add string", item);
160
+ lBlocks.push(item);
145
161
  } else {
146
162
  croak(`Bad item: ${OL(item)}`);
147
163
  }
148
- if (defined(subBlock)) {
149
- lBlocks.push(subBlock);
150
- }
151
164
  }
152
165
  block = arrayToBlock(lBlocks);
166
+ debug("block is", block);
153
167
  if (defined(setName)) {
154
168
  if (defined(proc = hReplacers[setName])) {
155
- debug(`REPLACE ${setName}`);
156
169
  block = proc(block);
170
+ debug(`REPLACE ${setName} with`, block);
157
171
  } else {
158
172
  debug(`NO REPLACER for ${setName}`);
159
173
  }
160
174
  }
161
175
  } else if (isSectionName(desc)) {
176
+ debug("item is a section name");
162
177
  block = this.section(desc).getBlock();
163
178
  if (defined(proc = hReplacers[desc])) {
164
179
  debug(`REPLACE ${desc}`);
@@ -167,7 +182,7 @@ export var SectionMap = class SectionMap {
167
182
  debug(`NO REPLACER for ${desc}`);
168
183
  }
169
184
  } else if (isSetName(desc)) {
170
- // --- pass array to getBlock()
185
+ debug("item is a set name");
171
186
  block = this.getBlock(this.hSets[desc], hReplacers);
172
187
  } else {
173
188
  croak(`Bad 1st arg: ${OL(desc)}`);
@@ -33,14 +33,6 @@ export pass = () ->
33
33
 
34
34
  # ---------------------------------------------------------------------------
35
35
 
36
- export getClassName = (obj) ->
37
-
38
- if (typeof obj != 'object')
39
- return undef
40
- return obj.constructor.name
41
-
42
- # ---------------------------------------------------------------------------
43
-
44
36
  export patchStr = (bigstr, pos, str) ->
45
37
 
46
38
  endpos = pos + str.length
@@ -62,6 +54,55 @@ export charCount = (str, ch) ->
62
54
 
63
55
  # ---------------------------------------------------------------------------
64
56
 
57
+ export isConstructor = (f) ->
58
+
59
+ try
60
+ new f()
61
+ catch err
62
+ if (err.message.indexOf('is not a constructor') >= 0)
63
+ return false;
64
+ return true;
65
+
66
+ # ---------------------------------------------------------------------------
67
+
68
+ export jsType = (x) ->
69
+
70
+ if notdefined(x)
71
+ return [undef, undef]
72
+ else if isString(x)
73
+ if x.match(/^\s*$/)
74
+ return ['string', 'empty']
75
+ else
76
+ return ['string', undef]
77
+ else if isNumber(x)
78
+ if Number.isInteger(x)
79
+ return ['number', 'integer']
80
+ else
81
+ return ['number', undef]
82
+ else if isBoolean(x)
83
+ return ['boolean', undef]
84
+ else if isHash(x)
85
+ lKeys = Object.keys(x);
86
+ if (lKeys.length == 0)
87
+ return ['hash', 'empty']
88
+ else
89
+ return ['hash', undef]
90
+ else if isArray(x)
91
+ if (x.length == 0)
92
+ return ['array', 'empty']
93
+ else
94
+ return ['array', undef]
95
+ else if isConstructor(x)
96
+ return ['function', 'constructor']
97
+ else if isFunction(x)
98
+ return ['function', undef]
99
+ else if isObject(x)
100
+ return ['object', undef]
101
+ else
102
+ croak "Unknown type: #{OL(x)}"
103
+
104
+ # ---------------------------------------------------------------------------
105
+
65
106
  export isString = (x) ->
66
107
 
67
108
  return (typeof x == 'string') || (x instanceof String)
@@ -94,6 +135,14 @@ export isObject = (x) ->
94
135
 
95
136
  # ---------------------------------------------------------------------------
96
137
 
138
+ export getClassName = (obj) ->
139
+
140
+ if (typeof obj != 'object')
141
+ return undef
142
+ return obj.constructor.name
143
+
144
+ # ---------------------------------------------------------------------------
145
+
97
146
  export isArray = (x) ->
98
147
 
99
148
  return Array.isArray(x)
@@ -207,13 +256,15 @@ export isRegExp = (x) ->
207
256
 
208
257
  # ---------------------------------------------------------------------------
209
258
 
210
- export isNumber = (x, hOptions={}) ->
259
+ export isNumber = (x, hOptions=undef) ->
211
260
 
212
261
  result = (typeof x == 'number') || (x instanceof Number)
213
- if result
214
- if defined(hOptions.min) && (x < hOptions.min)
262
+ if result && defined(hOptions)
263
+ assert isHash(hOptions), "2nd arg not a hash: #{OL(hOptions)}"
264
+ {min, max} = hOptions
265
+ if defined(min) && (x < min)
215
266
  result = false
216
- if defined(hOptions.max) && (x > hOptions.max)
267
+ if defined(max) && (x > max)
217
268
  result = false
218
269
  return result
219
270
 
@@ -37,14 +37,6 @@ export var eval_expr = function(str) {
37
37
  // pass - do nothing
38
38
  export var pass = function() {};
39
39
 
40
- // ---------------------------------------------------------------------------
41
- export var getClassName = function(obj) {
42
- if (typeof obj !== 'object') {
43
- return undef;
44
- }
45
- return obj.constructor.name;
46
- };
47
-
48
40
  // ---------------------------------------------------------------------------
49
41
  export var patchStr = function(bigstr, pos, str) {
50
42
  var endpos;
@@ -68,6 +60,63 @@ export var charCount = function(str, ch) {
68
60
  return count;
69
61
  };
70
62
 
63
+ // ---------------------------------------------------------------------------
64
+ export var isConstructor = function(f) {
65
+ var err;
66
+ try {
67
+ new f();
68
+ } catch (error1) {
69
+ err = error1;
70
+ if (err.message.indexOf('is not a constructor') >= 0) {
71
+ return false;
72
+ }
73
+ }
74
+ return true;
75
+ };
76
+
77
+ // ---------------------------------------------------------------------------
78
+ export var jsType = function(x) {
79
+ var lKeys;
80
+ if (notdefined(x)) {
81
+ return [undef, undef];
82
+ } else if (isString(x)) {
83
+ if (x.match(/^\s*$/)) {
84
+ return ['string', 'empty'];
85
+ } else {
86
+ return ['string', undef];
87
+ }
88
+ } else if (isNumber(x)) {
89
+ if (Number.isInteger(x)) {
90
+ return ['number', 'integer'];
91
+ } else {
92
+ return ['number', undef];
93
+ }
94
+ } else if (isBoolean(x)) {
95
+ return ['boolean', undef];
96
+ } else if (isHash(x)) {
97
+ lKeys = Object.keys(x);
98
+ if (lKeys.length === 0) {
99
+ return ['hash', 'empty'];
100
+ } else {
101
+ return ['hash', undef];
102
+ }
103
+ } else if (isArray(x)) {
104
+ if (x.length === 0) {
105
+ return ['array', 'empty'];
106
+ } else {
107
+ return ['array', undef];
108
+ }
109
+ } else if (isConstructor(x)) {
110
+ return ['function', 'constructor'];
111
+ } else if (isFunction(x)) {
112
+ return ['function', undef];
113
+ } else if (isObject(x)) {
114
+ return ['object', undef];
115
+ } else {
116
+ return croak(`Unknown type: ${OL(x)}`);
117
+ }
118
+ };
119
+
71
120
  // ---------------------------------------------------------------------------
72
121
  export var isString = function(x) {
73
122
  return (typeof x === 'string') || (x instanceof String);
@@ -94,6 +143,14 @@ export var isObject = function(x) {
94
143
  return (typeof x === 'object') && !isString(x) && !isArray(x) && !isHash(x) && !isNumber(x);
95
144
  };
96
145
 
146
+ // ---------------------------------------------------------------------------
147
+ export var getClassName = function(obj) {
148
+ if (typeof obj !== 'object') {
149
+ return undef;
150
+ }
151
+ return obj.constructor.name;
152
+ };
153
+
97
154
  // ---------------------------------------------------------------------------
98
155
  export var isArray = function(x) {
99
156
  return Array.isArray(x);
@@ -220,14 +277,16 @@ export var isRegExp = function(x) {
220
277
  };
221
278
 
222
279
  // ---------------------------------------------------------------------------
223
- export var isNumber = function(x, hOptions = {}) {
224
- var result;
280
+ export var isNumber = function(x, hOptions = undef) {
281
+ var max, min, result;
225
282
  result = (typeof x === 'number') || (x instanceof Number);
226
- if (result) {
227
- if (defined(hOptions.min) && (x < hOptions.min)) {
283
+ if (result && defined(hOptions)) {
284
+ assert(isHash(hOptions), `2nd arg not a hash: ${OL(hOptions)}`);
285
+ ({min, max} = hOptions);
286
+ if (defined(min) && (x < min)) {
228
287
  result = false;
229
288
  }
230
- if (defined(hOptions.max) && (x > hOptions.max)) {
289
+ if (defined(max) && (x > max)) {
231
290
  result = false;
232
291
  }
233
292
  }
@@ -0,0 +1,217 @@
1
+ # html_utils.coffee
2
+
3
+ import {assert, croak} from '@jdeighan/unit-tester/utils'
4
+ import {
5
+ undef, pass, words, isEmpty, nonEmpty,
6
+ } from '@jdeighan/coffee-utils'
7
+ import {indented, enclose} from '@jdeighan/coffee-utils/indent'
8
+ import {arrayToBlock} from '@jdeighan/coffee-utils/block'
9
+ import {debug} from '@jdeighan/coffee-utils/debug'
10
+
11
+ hNoEnd = {}
12
+ for tagName in words('area base br col command embed hr img input' \
13
+ + ' keygen link meta param source track wbr')
14
+ hNoEnd[tagName] = true
15
+
16
+ # ---------------------------------------------------------------------------
17
+
18
+ export parsetag = (line) ->
19
+
20
+ if lMatches = line.match(///^
21
+ (?:
22
+ ([A-Za-z][A-Za-z0-9_]*) # variable name
23
+ \s*
24
+ =
25
+ \s*
26
+ )? # variable is optional
27
+ ([A-Za-z][A-Za-z0-9_]*) # tag name
28
+ (?:
29
+ \:
30
+ ( [a-z]+ )
31
+ )?
32
+ (\S*) # modifiers (class names, etc.)
33
+ \s*
34
+ (.*) # attributes & enclosed text
35
+ $///)
36
+ [_, varName, tagName, subtype, modifiers, rest] = lMatches
37
+ else
38
+ croak "parsetag(): Invalid HTML: '#{line}'"
39
+
40
+ # --- Handle classes - subtypes and added via .<class>
41
+ lClasses = []
42
+ if nonEmpty(subtype) && (tagName != 'svelte')
43
+ lClasses.push subtype
44
+
45
+ if modifiers
46
+ # --- currently, these are only class names
47
+ while lMatches = modifiers.match(///^
48
+ \. ([A-Za-z][A-Za-z0-9_]*)
49
+ ///)
50
+ [all, className] = lMatches
51
+ lClasses.push className
52
+ modifiers = modifiers.substring(all.length)
53
+ if modifiers
54
+ croak "parsetag(): Invalid modifiers in '#{line}'"
55
+
56
+ # --- Handle attributes
57
+ hAttr = {} # { name: { value: <value>, quote: <quote> }, ... }
58
+
59
+ if varName
60
+ hAttr['bind:this'] = {value: varName, quote: '{'}
61
+
62
+ if (tagName == 'script') && (subtype == 'startup')
63
+ hAttr['context'] = {value: 'module', quote: '"'}
64
+
65
+ if rest
66
+ while lMatches = rest.match(///^
67
+ (?:
68
+ (?:
69
+ (?:
70
+ ( bind | on ) # prefix
71
+ :
72
+ )?
73
+ ([A-Za-z][A-Za-z0-9_]*) # attribute name
74
+ )
75
+ =
76
+ (?:
77
+ \{ ([^}]*) \} # attribute value
78
+ | " ([^"]*) "
79
+ | ' ([^']*) '
80
+ | ([^"'\s]+)
81
+ )
82
+ |
83
+ \{
84
+ ([A-Za-z][A-Za-z0-9_]*)
85
+ \}
86
+ ) \s*
87
+ ///)
88
+ [all, prefix, attrName, br_val, dq_val, sq_val, uq_val, ident] = lMatches
89
+ if ident
90
+ hAttr[ident] = { value: ident, shorthand: true }
91
+ else
92
+ if br_val
93
+ value = br_val
94
+ quote = '{'
95
+ else
96
+ assert ! prefix?, "prefix requires use of {...}"
97
+ if dq_val
98
+ value = dq_val
99
+ quote = '"'
100
+ else if sq_val
101
+ value = sq_val
102
+ quote = "'"
103
+ else
104
+ value = uq_val
105
+ quote = ''
106
+
107
+ if prefix
108
+ attrName = "#{prefix}:#{attrName}"
109
+
110
+ if attrName == 'class'
111
+ for className in value.split(/\s+/)
112
+ lClasses.push className
113
+ else
114
+ if hAttr.attrName?
115
+ croak "parsetag(): Multiple attributes named '#{attrName}'"
116
+ hAttr[attrName] = { value, quote }
117
+
118
+ rest = rest.substring(all.length)
119
+
120
+ # --- The rest is contained text
121
+ rest = rest.trim()
122
+ if lMatches = rest.match(///^
123
+ ['"]
124
+ (.*)
125
+ ['"]
126
+ $///)
127
+ rest = lMatches[1]
128
+
129
+ # --- Add class attribute to hAttr if there are classes
130
+ if (lClasses.length > 0)
131
+ hAttr.class = {
132
+ value: lClasses.join(' '),
133
+ quote: '"',
134
+ }
135
+
136
+ # --- Build the return value
137
+ hToken = {
138
+ type: 'tag'
139
+ tagName
140
+ }
141
+
142
+ if subtype
143
+ hToken.subtype = subtype
144
+ hToken.orgtag = "#{tagName}:#{subtype}"
145
+ else
146
+ hToken.orgtag = tagName
147
+
148
+ # --- if tagName == 'svelte', set hToken.tagName to hToken.orgtag
149
+ if (tagName == 'svelte')
150
+ hToken.tagName = hToken.orgtag
151
+
152
+ if nonEmpty(hAttr)
153
+ hToken.hAttr = hAttr
154
+
155
+ # --- Is there contained text?
156
+ if rest
157
+ hToken.text = rest
158
+
159
+ return hToken
160
+
161
+ # ---------------------------------------------------------------------------
162
+ # --- export only for unit testing
163
+
164
+ export attrStr = (hAttr) ->
165
+
166
+ if ! hAttr
167
+ return ''
168
+ str = ''
169
+ for attrName in Object.getOwnPropertyNames(hAttr)
170
+ {value, quote, shorthand} = hAttr[attrName]
171
+ if shorthand
172
+ str += " {#{value}}"
173
+ else
174
+ if quote == '{'
175
+ bquote = '{'
176
+ equote = '}'
177
+ else
178
+ bquote = equote = quote
179
+ str += " #{attrName}=#{bquote}#{value}#{equote}"
180
+ return str
181
+
182
+ # ---------------------------------------------------------------------------
183
+
184
+ export tag2str = (hToken, type='begin') ->
185
+
186
+ {tagName, hAttr} = hToken
187
+ if (type == 'begin')
188
+ str = "<#{tagName}" # build the string bit by bit
189
+ if nonEmpty(hAttr)
190
+ str += attrStr(hAttr)
191
+ str += '>'
192
+ return str
193
+ else if (type == 'end')
194
+ if hNoEnd[tagName]
195
+ return undef
196
+ else
197
+ return "</#{tagName}>"
198
+ else
199
+ croak "type must be 'begin' or 'end'"
200
+
201
+ # ---------------------------------------------------------------------------
202
+ # elem - indent text, surround with HTML tags
203
+
204
+ export elem = (tagName, hAttr=undef, text=undef, oneIndent="\t") ->
205
+
206
+ if isEmpty(text)
207
+ return undef
208
+ hToken = {
209
+ tagName
210
+ hAttr
211
+ text
212
+ }
213
+ return arrayToBlock([
214
+ tag2str(hToken, 'begin')
215
+ indented(text, 1, oneIndent)
216
+ tag2str(hToken, 'end')
217
+ ])
@@ -0,0 +1,222 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ // html_utils.coffee
3
+ var hNoEnd, i, len, ref, tagName;
4
+
5
+ import {
6
+ assert,
7
+ croak
8
+ } from '@jdeighan/unit-tester/utils';
9
+
10
+ import {
11
+ undef,
12
+ pass,
13
+ words,
14
+ isEmpty,
15
+ nonEmpty
16
+ } from '@jdeighan/coffee-utils';
17
+
18
+ import {
19
+ indented,
20
+ enclose
21
+ } from '@jdeighan/coffee-utils/indent';
22
+
23
+ import {
24
+ arrayToBlock
25
+ } from '@jdeighan/coffee-utils/block';
26
+
27
+ import {
28
+ debug
29
+ } from '@jdeighan/coffee-utils/debug';
30
+
31
+ hNoEnd = {};
32
+
33
+ ref = words('area base br col command embed hr img input' + ' keygen link meta param source track wbr');
34
+ for (i = 0, len = ref.length; i < len; i++) {
35
+ tagName = ref[i];
36
+ hNoEnd[tagName] = true;
37
+ }
38
+
39
+ // ---------------------------------------------------------------------------
40
+ export var parsetag = function(line) {
41
+ var _, all, attrName, br_val, className, dq_val, hAttr, hToken, ident, j, lClasses, lMatches, len1, modifiers, prefix, quote, ref1, rest, sq_val, subtype, uq_val, value, varName;
42
+ if (lMatches = line.match(/^(?:([A-Za-z][A-Za-z0-9_]*)\s*=\s*)?([A-Za-z][A-Za-z0-9_]*)(?:\:([a-z]+))?(\S*)\s*(.*)$/)) { // variable name
43
+ // variable is optional
44
+ // tag name
45
+ // modifiers (class names, etc.)
46
+ // attributes & enclosed text
47
+ [_, varName, tagName, subtype, modifiers, rest] = lMatches;
48
+ } else {
49
+ croak(`parsetag(): Invalid HTML: '${line}'`);
50
+ }
51
+ // --- Handle classes - subtypes and added via .<class>
52
+ lClasses = [];
53
+ if (nonEmpty(subtype) && (tagName !== 'svelte')) {
54
+ lClasses.push(subtype);
55
+ }
56
+ if (modifiers) {
57
+ // --- currently, these are only class names
58
+ while (lMatches = modifiers.match(/^\.([A-Za-z][A-Za-z0-9_]*)/)) {
59
+ [all, className] = lMatches;
60
+ lClasses.push(className);
61
+ modifiers = modifiers.substring(all.length);
62
+ }
63
+ if (modifiers) {
64
+ croak(`parsetag(): Invalid modifiers in '${line}'`);
65
+ }
66
+ }
67
+ // --- Handle attributes
68
+ hAttr = {}; // { name: { value: <value>, quote: <quote> }, ... }
69
+ if (varName) {
70
+ hAttr['bind:this'] = {
71
+ value: varName,
72
+ quote: '{'
73
+ };
74
+ }
75
+ if ((tagName === 'script') && (subtype === 'startup')) {
76
+ hAttr['context'] = {
77
+ value: 'module',
78
+ quote: '"'
79
+ };
80
+ }
81
+ if (rest) {
82
+ while (lMatches = rest.match(/^(?:(?:(?:(bind|on):)?([A-Za-z][A-Za-z0-9_]*))=(?:\{([^}]*)\}|"([^"]*)"|'([^']*)'|([^"'\s]+))|\{([A-Za-z][A-Za-z0-9_]*)\})\s*/)) { // prefix
83
+ // attribute name
84
+ // attribute value
85
+ [all, prefix, attrName, br_val, dq_val, sq_val, uq_val, ident] = lMatches;
86
+ if (ident) {
87
+ hAttr[ident] = {
88
+ value: ident,
89
+ shorthand: true
90
+ };
91
+ } else {
92
+ if (br_val) {
93
+ value = br_val;
94
+ quote = '{';
95
+ } else {
96
+ assert(prefix == null, "prefix requires use of {...}");
97
+ if (dq_val) {
98
+ value = dq_val;
99
+ quote = '"';
100
+ } else if (sq_val) {
101
+ value = sq_val;
102
+ quote = "'";
103
+ } else {
104
+ value = uq_val;
105
+ quote = '';
106
+ }
107
+ }
108
+ if (prefix) {
109
+ attrName = `${prefix}:${attrName}`;
110
+ }
111
+ if (attrName === 'class') {
112
+ ref1 = value.split(/\s+/);
113
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
114
+ className = ref1[j];
115
+ lClasses.push(className);
116
+ }
117
+ } else {
118
+ if (hAttr.attrName != null) {
119
+ croak(`parsetag(): Multiple attributes named '${attrName}'`);
120
+ }
121
+ hAttr[attrName] = {value, quote};
122
+ }
123
+ }
124
+ rest = rest.substring(all.length);
125
+ }
126
+ }
127
+ // --- The rest is contained text
128
+ rest = rest.trim();
129
+ if (lMatches = rest.match(/^['"](.*)['"]$/)) {
130
+ rest = lMatches[1];
131
+ }
132
+ // --- Add class attribute to hAttr if there are classes
133
+ if (lClasses.length > 0) {
134
+ hAttr.class = {
135
+ value: lClasses.join(' '),
136
+ quote: '"'
137
+ };
138
+ }
139
+ // --- Build the return value
140
+ hToken = {
141
+ type: 'tag',
142
+ tagName
143
+ };
144
+ if (subtype) {
145
+ hToken.subtype = subtype;
146
+ hToken.orgtag = `${tagName}:${subtype}`;
147
+ } else {
148
+ hToken.orgtag = tagName;
149
+ }
150
+ // --- if tagName == 'svelte', set hToken.tagName to hToken.orgtag
151
+ if (tagName === 'svelte') {
152
+ hToken.tagName = hToken.orgtag;
153
+ }
154
+ if (nonEmpty(hAttr)) {
155
+ hToken.hAttr = hAttr;
156
+ }
157
+ // --- Is there contained text?
158
+ if (rest) {
159
+ hToken.text = rest;
160
+ }
161
+ return hToken;
162
+ };
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // --- export only for unit testing
166
+ export var attrStr = function(hAttr) {
167
+ var attrName, bquote, equote, j, len1, quote, ref1, shorthand, str, value;
168
+ if (!hAttr) {
169
+ return '';
170
+ }
171
+ str = '';
172
+ ref1 = Object.getOwnPropertyNames(hAttr);
173
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
174
+ attrName = ref1[j];
175
+ ({value, quote, shorthand} = hAttr[attrName]);
176
+ if (shorthand) {
177
+ str += ` {${value}}`;
178
+ } else {
179
+ if (quote === '{') {
180
+ bquote = '{';
181
+ equote = '}';
182
+ } else {
183
+ bquote = equote = quote;
184
+ }
185
+ str += ` ${attrName}=${bquote}${value}${equote}`;
186
+ }
187
+ }
188
+ return str;
189
+ };
190
+
191
+ // ---------------------------------------------------------------------------
192
+ export var tag2str = function(hToken, type = 'begin') {
193
+ var hAttr, str;
194
+ ({tagName, hAttr} = hToken);
195
+ if (type === 'begin') {
196
+ str = `<${tagName}`;
197
+ if (nonEmpty(hAttr)) {
198
+ str += attrStr(hAttr);
199
+ }
200
+ str += '>';
201
+ return str;
202
+ } else if (type === 'end') {
203
+ if (hNoEnd[tagName]) {
204
+ return undef;
205
+ } else {
206
+ return `</${tagName}>`;
207
+ }
208
+ } else {
209
+ return croak("type must be 'begin' or 'end'");
210
+ }
211
+ };
212
+
213
+ // ---------------------------------------------------------------------------
214
+ // elem - indent text, surround with HTML tags
215
+ export var elem = function(tagName, hAttr = undef, text = undef, oneIndent = "\t") {
216
+ var hToken;
217
+ if (isEmpty(text)) {
218
+ return undef;
219
+ }
220
+ hToken = {tagName, hAttr, text};
221
+ return arrayToBlock([tag2str(hToken, 'begin'), indented(text, 1, oneIndent), tag2str(hToken, 'end')]);
222
+ };
@@ -164,14 +164,3 @@ export enclose = (text, pre, post, oneIndent="\t") ->
164
164
  indented(text, 1, oneIndent)
165
165
  post
166
166
  ])
167
-
168
- # ---------------------------------------------------------------------------
169
- # elem - indent text, surround with HTML tags
170
-
171
- export elem = (text, tag, oneIndent="\t") ->
172
-
173
- return arrayToBlock([
174
- "<#{tag}>"
175
- indented(text, 1, oneIndent)
176
- "</#{tag}>"
177
- ])
@@ -187,9 +187,3 @@ export var untabify = function(str, numSpaces = 3) {
187
187
  export var enclose = function(text, pre, post, oneIndent = "\t") {
188
188
  return arrayToBlock([pre, indented(text, 1, oneIndent), post]);
189
189
  };
190
-
191
- // ---------------------------------------------------------------------------
192
- // elem - indent text, surround with HTML tags
193
- export var elem = function(text, tag, oneIndent = "\t") {
194
- return arrayToBlock([`<${tag}>`, indented(text, 1, oneIndent), `</${tag}>`]);
195
- };
@@ -183,7 +183,12 @@ export logItem = (label, item, pre='', itemPre=undef) ->
183
183
  putstr "#{pre}#{labelStr}#{item}"
184
184
  else if isString(item)
185
185
  if (item.length <= maxOneLine)
186
- putstr "#{pre}#{labelStr}'#{escapeStr(item)}'"
186
+ result = escapeStr(item)
187
+ hasApos = (result.indexOf("'") >= 0)
188
+ if hasApos
189
+ putstr "#{pre}#{labelStr}\"#{result}\""
190
+ else
191
+ putstr "#{pre}#{labelStr}'#{result}'"
187
192
  else
188
193
  if label
189
194
  putstr "#{pre}#{label}:"
package/src/log_utils.js CHANGED
@@ -184,7 +184,7 @@ export var logBareItem = function(item, pre = '') {
184
184
 
185
185
  // ---------------------------------------------------------------------------
186
186
  export var logItem = function(label, item, pre = '', itemPre = undef) {
187
- var i, labelStr, len, ref, str;
187
+ var hasApos, i, labelStr, len, ref, result, str;
188
188
  assert(isString(pre), `not a string: ${OL(pre)}`);
189
189
  assert(isFunction(putstr), "putstr not properly set");
190
190
  assert(!label || isString(label), "label a non-string");
@@ -207,7 +207,13 @@ export var logItem = function(label, item, pre = '', itemPre = undef) {
207
207
  putstr(`${pre}${labelStr}${item}`);
208
208
  } else if (isString(item)) {
209
209
  if (item.length <= maxOneLine) {
210
- putstr(`${pre}${labelStr}'${escapeStr(item)}'`);
210
+ result = escapeStr(item);
211
+ hasApos = result.indexOf("'") >= 0;
212
+ if (hasApos) {
213
+ putstr(`${pre}${labelStr}\"${result}\"`);
214
+ } else {
215
+ putstr(`${pre}${labelStr}'${result}'`);
216
+ }
211
217
  } else {
212
218
  if (label) {
213
219
  putstr(`${pre}${label}:`);