@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 +2 -1
- package/src/SectionMap.coffee +24 -11
- package/src/SectionMap.js +27 -12
- package/src/coffee_utils.coffee +63 -12
- package/src/coffee_utils.js +72 -13
- package/src/html_utils.coffee +217 -0
- package/src/html_utils.js +222 -0
- package/src/indent_utils.coffee +0 -11
- package/src/indent_utils.js +0 -6
- package/src/log_utils.coffee +6 -1
- package/src/log_utils.js +8 -2
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@jdeighan/coffee-utils",
|
3
3
|
"type": "module",
|
4
|
-
"version": "10.0.
|
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",
|
package/src/SectionMap.coffee
CHANGED
@@ -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
|
108
|
-
|
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
|
114
|
-
subBlock = item
|
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
|
-
|
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
|
-
|
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 (
|
138
|
-
|
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 (
|
144
|
-
subBlock = item;
|
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
|
-
|
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)}`);
|
package/src/coffee_utils.coffee
CHANGED
@@ -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
|
-
|
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(
|
267
|
+
if defined(max) && (x > max)
|
217
268
|
result = false
|
218
269
|
return result
|
219
270
|
|
package/src/coffee_utils.js
CHANGED
@@ -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
|
-
|
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(
|
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
|
+
};
|
package/src/indent_utils.coffee
CHANGED
@@ -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
|
-
])
|
package/src/indent_utils.js
CHANGED
@@ -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
|
-
};
|
package/src/log_utils.coffee
CHANGED
@@ -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
|
-
|
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
|
-
|
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}:`);
|