@jdeighan/coffee-utils 10.0.2 → 10.0.5

Sign up to get free protection for your applications and to get access to all the features.
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}:`);