diff --git a/src/js/jssm-dot.peg b/src/js/jssm-dot.peg
index f9960e1c..30e5658d 100644
--- a/src/js/jssm-dot.peg
+++ b/src/js/jssm-dot.peg
@@ -40,7 +40,8 @@ QuoteMark = '"'
Unescaped = [\x20-\x21\x23-\x5B\x5D-\u10FFFF]
Label "label"
- = text:[0-9a-zA-Z\.\+\-\_]+ { return text.join(''); }
+ = text:[0-9a-zA-Z\.\+\-\_\&\(\)\%\$\#\@\!\?\<\>\,\:\;\']+ { return text.join(''); }
+ \ text:String { return text.join(''); }
DescValue
= string:String
diff --git a/src/js/jssm-viz.js b/src/js/jssm-viz.js
new file mode 100644
index 00000000..220854ab
--- /dev/null
+++ b/src/js/jssm-viz.js
@@ -0,0 +1,108 @@
+
+const viz = jssm => {
+
+ const l_states = jssm.states();
+
+ const node_of = state => `n${l_states.indexOf(state)}`,
+ vc = col => (jssm._viz_colors:any)[col] || '';
+
+ const nodes : string = l_states.map( (s:any) => {
+
+ const this_state = jssm.state_for(s),
+ terminal = jssm.state_is_terminal(s),
+ final = jssm.state_is_final(s),
+ complete = jssm.state_is_complete(s),
+ features = [
+ ['label', s],
+ ['peripheries', complete? 2 : 1 ],
+ ['fillcolor', final ? vc('fill_final') :
+ (complete? vc('fill_complete') :
+ (terminal? vc('fill_terminal') :
+ '')) ]
+ ]
+ .filter(r => r[1])
+ .map( r => `${r[0]}="${r[1]}"`)
+ .join(' ');
+ return `${node_of(s)} [${features}];`;
+
+ }).join(' ');
+
+ const strike = [];
+ const edges = jssm.states().map( (s:any) =>
+
+ jssm.exits_for(s).map( (ex:any) => {
+
+ if ( strike.find(row => (row[0] === s) && (row[1] == ex) ) ) {
+ return ''; // already did the pair
+ }
+
+ const edge = jssm.transition_for(s, ex),
+ pair = jssm.transition_for(ex, s),
+ double = pair && (s !== ex),
+
+ head_state = jssm.state_for(s),
+ tail_state = jssm.state_for(ex),
+
+// label = edge ? ([edge.name?`${(edge.name:any)}`:undefined,`${(edge.probability:any)}`]
+// .filter(not_undef => !!not_undef)
+// .join('\n') || undefined
+// ) : undefined,
+
+ if_obj_field = (obj:any, field) => obj? (obj[field] || '') : '',
+
+ h_final = jssm.state_is_final(s),
+ h_complete = jssm.state_is_complete(s),
+ h_terminal = jssm.state_is_terminal(s),
+
+ t_final = jssm.state_is_final(ex),
+ t_complete = jssm.state_is_complete(ex),
+ t_terminal = jssm.state_is_terminal(ex),
+
+ lineColor = (final, complete, terminal, _solo_1_2 = '_solo') =>
+ final ? (vc('line_final' + _solo_1_2)) :
+ (complete? (vc('line_complete' + _solo_1_2)) :
+ (terminal? (vc('line_terminal' + _solo_1_2)) :
+ vc('normal_line' + _solo_1_2))),
+
+ textColor = (final, complete, terminal, _solo_1_2 = '_solo') : string =>
+ final ? (vc('text_final' + _solo_1_2)) :
+ (complete? (vc('text_complete' + _solo_1_2)) :
+ (terminal? (vc('text_terminal' + _solo_1_2)) :
+ '')),
+
+ headColor = textColor(h_final, h_complete, h_terminal, double? '_1' : '_solo'),
+ tailColor = textColor(t_final, t_complete, t_terminal, double? '_2' : '_solo'),
+
+ labelInline = [
+// [edge, 'name', 'label', true],
+ [pair, 'probability', 'headlabel', 'name', 'action', double, headColor],
+ [edge, 'probability', 'taillabel', 'name', 'action', true, tailColor]
+ ]
+ .map( r => ({ which: r[2], whether: (r[5]? ([(if_obj_field(r[0], r[5]):any), (if_obj_field(r[0], r[1]):any), (if_obj_field(r[0], r[3]):any)].filter(q=>q).join('
') || '') : ''), color: r[6] }) )
+ .filter( present => present.whether )
+ .map( r => `${r.which}=${(r.color)? `<${(r.whether : any)}>` : `"${(r.whether : any)}"`};`)
+ .join(' '),
+
+ tc1 = lineColor(t_final, t_complete, t_terminal, '_1'),
+ tc2 = lineColor(h_final, h_complete, h_terminal, '_2'),
+ tcd = lineColor(t_final, t_complete, t_terminal, '_solo'),
+
+ edgeInline = edge ? (double? `dir=both;color="${tc1}:${tc2}"` : `color="${tcd}"`) : '';
+
+ if (pair) { strike.push([ex, s]); }
+
+ return `${node_of(s)}->${node_of(ex)} [${labelInline}${edgeInline}];`;
+
+ }).join(' ')
+
+ ).join(' ');
+
+ return `digraph G {\n fontname="helvetica neue";\n style=filled;\n bgcolor=lightgrey;\n node [fontsize=14; shape=box; style=filled; fillcolor=white; fontname="helvetica neue"];\n edge [fontsize=6;fontname="helvetica neue"];\n\n ${nodes}\n\n ${edges}\n}`;
+
+}
+
+
+
+
+
+export { viz };
diff --git a/src/js/jssm.js b/src/js/jssm.js
index e8138077..ad9acc00 100644
--- a/src/js/jssm.js
+++ b/src/js/jssm.js
@@ -497,109 +497,6 @@ todo comeback
- viz() {
-
- const l_states = this.states();
-
- const node_of = state => `n${l_states.indexOf(state)}`,
- vc = col => (this._viz_colors:any)[col] || '';
-
- const nodes : string = l_states.map( (s:any) => {
-
- const this_state = this.state_for(s),
- terminal = this.state_is_terminal(s),
- final = this.state_is_final(s),
- complete = this.state_is_complete(s),
- features = [
- ['label', s],
- ['peripheries', complete? 2 : 1 ],
- ['fillcolor', final ? vc('fill_final') :
- (complete? vc('fill_complete') :
- (terminal? vc('fill_terminal') :
- '')) ]
- ]
- .filter(r => r[1])
- .map( r => `${r[0]}="${r[1]}"`)
- .join(' ');
- return `${node_of(s)} [${features}];`;
-
- }).join(' ');
-
- const strike = [];
- const edges = this.states().map( (s:any) =>
-
- this.exits_for(s).map( (ex:any) => {
-
- if ( strike.find(row => (row[0] === s) && (row[1] == ex) ) ) {
- return ''; // already did the pair
- }
-
- const edge = this.transition_for(s, ex),
- pair = this.transition_for(ex, s),
- double = pair && (s !== ex),
-
- head_state = this.state_for(s),
- tail_state = this.state_for(ex),
-
-// label = edge ? ([edge.name?`${(edge.name:any)}`:undefined,`${(edge.probability:any)}`]
-// .filter(not_undef => !!not_undef)
-// .join('\n') || undefined
-// ) : undefined,
-
- if_obj_field = (obj:any, field) => obj? (obj[field] || '') : '',
-
- h_final = this.state_is_final(s),
- h_complete = this.state_is_complete(s),
- h_terminal = this.state_is_terminal(s),
-
- t_final = this.state_is_final(ex),
- t_complete = this.state_is_complete(ex),
- t_terminal = this.state_is_terminal(ex),
-
- lineColor = (final, complete, terminal, _solo_1_2 = '_solo') =>
- final ? (vc('line_final' + _solo_1_2)) :
- (complete? (vc('line_complete' + _solo_1_2)) :
- (terminal? (vc('line_terminal' + _solo_1_2)) :
- vc('normal_line' + _solo_1_2))),
-
- textColor = (final, complete, terminal, _solo_1_2 = '_solo') : string =>
- final ? (vc('text_final' + _solo_1_2)) :
- (complete? (vc('text_complete' + _solo_1_2)) :
- (terminal? (vc('text_terminal' + _solo_1_2)) :
- '')),
-
- headColor = textColor(h_final, h_complete, h_terminal, double? '_1' : '_solo'),
- tailColor = textColor(t_final, t_complete, t_terminal, double? '_2' : '_solo'),
-
- labelInline = [
-// [edge, 'name', 'label', true],
- [pair, 'probability', 'headlabel', 'name', 'action', double, headColor],
- [edge, 'probability', 'taillabel', 'name', 'action', true, tailColor]
- ]
- .map( r => ({ which: r[2], whether: (r[5]? ([(if_obj_field(r[0], r[5]):any), (if_obj_field(r[0], r[1]):any), (if_obj_field(r[0], r[3]):any)].filter(q=>q).join('
') || '') : ''), color: r[6] }) )
- .filter( present => present.whether )
- .map( r => `${r.which}=${(r.color)? `<${(r.whether : any)}>` : `"${(r.whether : any)}"`};`)
- .join(' '),
-
- tc1 = lineColor(t_final, t_complete, t_terminal, '_1'),
- tc2 = lineColor(h_final, h_complete, h_terminal, '_2'),
- tcd = lineColor(t_final, t_complete, t_terminal, '_solo'),
-
- edgeInline = edge ? (double? `dir=both;color="${tc1}:${tc2}"` : `color="${tcd}"`) : '';
-
- if (pair) { strike.push([ex, s]); }
-
- return `${node_of(s)}->${node_of(ex)} [${labelInline}${edgeInline}];`;
-
- }).join(' ')
-
- ).join(' ');
-
- return `digraph G {\n fontname="helvetica neue";\n style=filled;\n bgcolor=lightgrey;\n node [fontsize=14; shape=box; style=filled; fillcolor=white; fontname="helvetica neue"];\n edge [fontsize=6;fontname="helvetica neue"];\n\n ${nodes}\n\n ${edges}\n}`;
-
- }
-
-
}