@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 +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}:`);
|