book-source 0.1.1 → 0.2.1

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/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
 
2
2
  # Book Source
3
3
 
4
- A Lightweight Markup Language similar to Markdown.
4
+ Book Source is a Lightweight Markup Language similar to Markdown.
5
5
 
6
- This lib parses Book Source and is shipped with a HTML renderer.
6
+ This is the parser, you may want to add a renderer, like the [Book Source HTML renderer](https://github.com/cronvel/book-source-html-renderer).
7
7
 
8
8
  New renderers can easily be created.
9
9
 
package/documentation.md CHANGED
@@ -1,9 +1,9 @@
1
1
 
2
2
  # Book Source
3
3
 
4
- A Lightweight Markup Language similar to Markdown.
4
+ Book Source is a Lightweight Markup Language similar to Markdown.
5
5
 
6
- This lib parses Book Source and is shipped with a HTML renderer.
6
+ This is the parser, you may want to add a renderer, like the [Book Source HTML renderer](https://github.com/cronvel/book-source-html-renderer).
7
7
 
8
8
  New renderers can easily be created.
9
9
 
@@ -28,10 +28,10 @@
28
28
 
29
29
 
30
30
 
31
- const HtmlRenderer = require( './HtmlRenderer.js' ) ;
31
+ const documentParts = require( './documentParts.js' ) ;
32
32
  const Style = require( './Style.js' ) ;
33
+ const Theme = require( './Theme.js' ) ;
33
34
 
34
- const emoji = require( 'string-kit/lib/emoji.js' ) ;
35
35
  const inPlaceFilter = require( 'array-kit/lib/inPlaceFilter.js' ) ;
36
36
 
37
37
 
@@ -47,12 +47,6 @@ module.exports = StructuredDocument ;
47
47
 
48
48
 
49
49
 
50
- StructuredDocument.prototype.toHtml = function( theme , params ) {
51
- return this.render( new HtmlRenderer( theme , params ) ) ;
52
- } ;
53
-
54
-
55
-
56
50
  StructuredDocument.prototype.render = function( renderer ) {
57
51
  var meta = {
58
52
  title: this.title
@@ -73,33 +67,33 @@ StructuredDocument.prototype.renderParts = function( renderer , parts , partStac
73
67
  var str = '' , index = 0 ;
74
68
 
75
69
  for ( let part of parts ) {
76
- let childrenStr = '' ;
70
+ let partsStr = '' ;
77
71
 
78
- if ( part.children ) {
72
+ if ( part.parts ) {
79
73
  if ( renderer.group?.[ part.type ] ) {
80
- // Some renderer have to group children an apply things to each group,
74
+ // Some renderer have to group child parts an apply things to each group,
81
75
  // E.g. HTML groups 'tableHeader' to produce <thead> and 'tableRow' to produce <tbody>
82
- let groupList = this.groupByType( part.children , renderer.group[ part.type ] ) ;
76
+ let groupList = this.groupByType( part.parts , renderer.group[ part.type ] ) ;
83
77
  //console.error( "groupList:" , groupList ) ;
84
78
 
85
79
  for ( let group of groupList ) {
86
80
  if ( renderer.group[ part.type ][ group.type ] ) {
87
81
  partStack.push( part ) ;
88
- let groupStr = this.renderParts( renderer , group.children , partStack ) ;
82
+ let groupStr = this.renderParts( renderer , group.parts , partStack ) ;
89
83
  partStack.pop() ;
90
- childrenStr += renderer.group[ part.type ][ group.type ]( part , groupStr , partStack ) ;
84
+ partsStr += renderer.group[ part.type ][ group.type ]( part , groupStr , partStack ) ;
91
85
  }
92
86
  }
93
87
  }
94
88
  else {
95
89
  partStack.push( part ) ;
96
- childrenStr = this.renderParts( renderer , part.children , [ ... partStack , part ] ) ;
90
+ partsStr = this.renderParts( renderer , part.parts , [ ... partStack , part ] ) ;
97
91
  partStack.pop() ;
98
92
  }
99
93
  }
100
94
 
101
95
  if ( renderer[ part.type ] ) {
102
- str += renderer[ part.type ]( part , childrenStr , partStack , index ) ;
96
+ str += renderer[ part.type ]( part , partsStr , partStack , index ) ;
103
97
  }
104
98
 
105
99
  index ++ ;
@@ -118,13 +112,13 @@ StructuredDocument.prototype.groupByType = function( parts , rendererGroup ) {
118
112
  if ( ! group ) {
119
113
  group = groupMap[ part.type ] = {
120
114
  type: part.type ,
121
- children: [] ,
115
+ parts: [] ,
122
116
  order: + rendererGroup[ part.type ]?.order || 0
123
117
  } ;
124
118
  groupList.push( group ) ;
125
119
  }
126
120
 
127
- group.children.push( part ) ;
121
+ group.parts.push( part ) ;
128
122
  }
129
123
 
130
124
  groupList.sort( ( a , b ) => a.order - b.order ) ;
@@ -143,7 +137,7 @@ StructuredDocument.prototype.autoTitle = function() {
143
137
 
144
138
  if ( ! header ) { return ; }
145
139
 
146
- str = this.getText( header.children ) ;
140
+ str = this.getText( header.parts ) ;
147
141
  if ( str ) { this.title = str ; }
148
142
  } ;
149
143
 
@@ -154,7 +148,7 @@ StructuredDocument.prototype.getText = function( parts ) { //= this.parts ) {
154
148
 
155
149
  for ( let part of parts ) {
156
150
  if ( part.text ) { str += part.text ; }
157
- else if ( part.children?.length ) { str += this.getText( part.children ) ; }
151
+ else if ( part.parts?.length ) { str += this.getText( part.parts ) ; }
158
152
  }
159
153
 
160
154
  return str ;
@@ -238,7 +232,7 @@ function parseBlock( str , ctx ) {
238
232
  }
239
233
 
240
234
  if ( indentType === QUOTE_INDENT ) {
241
- ctx.parts.push( { type: 'quote' , indent: indentSpaces } ) ;
235
+ ctx.parts.push( new documentParts.Quote( indentSpaces ) ) ;
242
236
  stack( ctx ) ;
243
237
  }
244
238
  else if ( indentType <= 0 ) {
@@ -548,13 +542,13 @@ const PARAGRAPH_END_PARAMS = {
548
542
 
549
543
  function parseParagraph( str , ctx ) {
550
544
  //console.error( "parseParagraph in:" , ctx.i , str.slice( ctx.i ) ) ;
551
- ctx.parts.push( { type: 'paragraph' } ) ;
545
+ ctx.parts.push( new documentParts.Paragraph() ) ;
552
546
 
553
547
  var blockEnd = detectBlockEnd( str , ctx.i , ctx.parent?.indent , PARAGRAPH_END_PARAMS ) ;
554
548
  //console.error( "blockEnd:" , blockEnd ) ;
555
549
  parseInlineChildren( str , ctx , blockEnd ) ;
556
550
 
557
- //console.error( "children:" , children ) ;
551
+ //console.error( "parts:" , parts ) ;
558
552
  //console.error( "parseParagraph out:" , ctx.i , str.slice( ctx.i ) ) ;
559
553
  }
560
554
 
@@ -573,7 +567,7 @@ function parseHeader( str , ctx ) {
573
567
 
574
568
  ctx.i += streak ;
575
569
  if ( str[ ctx.i ] === ' ' ) { ctx.i ++ ; }
576
- ctx.parts.push( { type: 'header' , level: streak } ) ;
570
+ ctx.parts.push( new documentParts.Header( streak ) ) ;
577
571
 
578
572
  var blockEnd = detectBlockEnd( str , ctx.i , ctx.parent?.indent , HEADER_END_PARAMS ) ;
579
573
  parseInlineChildren( str , ctx , blockEnd ) ;
@@ -590,7 +584,7 @@ const CITE_END_PARAMS = {
590
584
 
591
585
  function parseCite( str , ctx ) {
592
586
  ctx.i += 3 ;
593
- ctx.parts.push( { type: 'cite' } ) ;
587
+ ctx.parts.push( new documentParts.Cite() ) ;
594
588
 
595
589
  var blockEnd = detectBlockEnd( str , ctx.i , ctx.parent?.indent , CITE_END_PARAMS ) ;
596
590
  parseInlineChildren( str , ctx , blockEnd ) ;
@@ -612,12 +606,12 @@ function parseListItem( str , ctx , indent ) {
612
606
  var lastPart = ctx.parts[ ctx.parts.length - 1 ] ;
613
607
 
614
608
  if ( ! lastPart || lastPart.type !== 'list' ) {
615
- ctx.parts.push( { type: 'list' , indent } ) ;
609
+ ctx.parts.push( new documentParts.List( indent ) ) ;
616
610
  }
617
611
 
618
612
  stack( ctx ) ;
619
613
 
620
- ctx.parts.push( { type: 'listItem' , indent } ) ;
614
+ ctx.parts.push( new documentParts.ListItem( indent ) ) ;
621
615
 
622
616
  var blockEnd = detectBlockEnd( str , ctx.i , ctx.parent?.indent , LIST_ITEM_END_PARAMS ) ;
623
617
  parseInlineChildren( str , ctx , blockEnd ) ;
@@ -635,12 +629,12 @@ function parseOrderedListItem( str , ctx , indent ) {
635
629
  var lastPart = ctx.parts[ ctx.parts.length - 1 ] ;
636
630
 
637
631
  if ( ! lastPart || lastPart.type !== 'orderedList' ) {
638
- ctx.parts.push( { type: 'orderedList' , indent } ) ;
632
+ ctx.parts.push( new documentParts.OrderedList( indent ) ) ;
639
633
  }
640
634
 
641
635
  stack( ctx ) ;
642
636
 
643
- ctx.parts.push( { type: 'orderedListItem' , indent , order } ) ;
637
+ ctx.parts.push( new documentParts.OrderedListItem( indent , order ) ) ;
644
638
 
645
639
  var blockEnd = detectBlockEnd( str , ctx.i , ctx.parent?.indent , LIST_ITEM_END_PARAMS ) ;
646
640
  parseInlineChildren( str , ctx , blockEnd ) ;
@@ -701,7 +695,7 @@ function parseMedia( str , ctx , float = null ) {
701
695
 
702
696
 
703
697
  function parseHorizontalRule( str , ctx ) {
704
- var params = { type: 'horizontalRule' } ,
698
+ var clearFloat = false ,
705
699
  streak = countStreak( str , ctx.i , '-' ) ;
706
700
 
707
701
  if (
@@ -711,10 +705,10 @@ function parseHorizontalRule( str , ctx ) {
711
705
  && str[ ctx.i + streak + 3 ] === '-'
712
706
  && str[ ctx.i + streak + 4 ] === '-'
713
707
  ) {
714
- params.clearFloat = true ;
708
+ clearFloat = true ;
715
709
  }
716
710
 
717
- ctx.parts.push( params ) ;
711
+ ctx.parts.push( new documentParts.HorizontalRule( clearFloat ) ) ;
718
712
  ctx.i = searchEndOfLine( str , ctx.i ) + 1 ;
719
713
  }
720
714
 
@@ -724,7 +718,7 @@ function parseClearFloat( str , ctx ) {
724
718
  var streak = countStreak( str , ctx.i + 1 , '-' ) ;
725
719
 
726
720
  if ( str[ ctx.i + 1 + streak ] === '>' ) {
727
- ctx.parts.push( { type: 'clearFloat' } ) ;
721
+ ctx.parts.push( new documentParts.ClearFloat() ) ;
728
722
  ctx.i = searchEndOfLine( str , ctx.i ) + 1 ;
729
723
  }
730
724
  else {
@@ -746,12 +740,10 @@ function parseCodeBlock( str , ctx ) {
746
740
  return parseParagraph( str , ctx ) ;
747
741
  }
748
742
 
749
- var [ contentEnd , blockEnd ] = ends ;
743
+ var [ contentEnd , blockEnd ] = ends ,
744
+ text = str.slice( contentStart , contentEnd - 1 ) ; // We strip the last newline
750
745
 
751
- var params = { type: 'codeBlock' } ;
752
- if ( lang ) { params.lang = lang ; }
753
- params.text = str.slice( contentStart , contentEnd - 1 ) ; // We strip the last newline
754
- ctx.parts.push( params ) ;
746
+ ctx.parts.push( new documentParts.CodeBlock( text , lang ) ) ;
755
747
  ctx.i = blockEnd ;
756
748
  }
757
749
 
@@ -784,10 +776,7 @@ function parseAnchor( str , ctx ) {
784
776
  var end = searchCloser( str , ctx.i + 2 , '(' , ')' , true ) ;
785
777
  if ( end < 0 ) { return parseParagraph( str , ctx ) ; }
786
778
 
787
- ctx.parts.push( {
788
- type: 'anchor' ,
789
- href: str.slice( ctx.i + 2 , end )
790
- } ) ;
779
+ ctx.parts.push( new documentParts.Anchor( str.slice( ctx.i + 2 , end ) ) ) ;
791
780
 
792
781
  ctx.i = end + 1 ;
793
782
  }
@@ -808,7 +797,7 @@ function parseTableCaption( str , ctx ) {
808
797
  var table = ctx.parts[ ctx.parts.length - 1 ] ;
809
798
 
810
799
  if ( ! table || table.type !== 'table' || ctx.lastLineWasEmpty ) {
811
- table = { type: 'table' , columns: [] } ;
800
+ table = new documentParts.Table() ;
812
801
  ctx.parts.push( table ) ;
813
802
  }
814
803
 
@@ -819,7 +808,7 @@ function parseTableCaption( str , ctx ) {
819
808
  tableCaption = lastRow ;
820
809
 
821
810
  if ( ! lastRow || lastRow.type !== 'tableCaption' ) {
822
- tableCaption = { type: 'tableCaption' } ;
811
+ tableCaption = new documentParts.TableCaption() ;
823
812
  ctx.parts.push( tableCaption ) ;
824
813
  }
825
814
 
@@ -848,7 +837,7 @@ function parseTableRow( str , ctx ) {
848
837
  mergeMode = false ;
849
838
 
850
839
  if ( ! table || table.type !== 'table' || ctx.lastLineWasEmpty ) {
851
- table = { type: 'table' , columns: [] , children: [] } ;
840
+ table = new documentParts.Table() ;
852
841
  ctx.parts.push( table ) ;
853
842
  }
854
843
 
@@ -860,7 +849,7 @@ function parseTableRow( str , ctx ) {
860
849
  tableRow = lastRow ;
861
850
 
862
851
  if ( ! lastRow || lastRow.type !== 'tableRow' ) {
863
- tableRow = { type: 'tableRow' } ;
852
+ tableRow = new documentParts.TableRow() ;
864
853
  ctx.parts.push( tableRow ) ;
865
854
  }
866
855
  else {
@@ -868,7 +857,7 @@ function parseTableRow( str , ctx ) {
868
857
  }
869
858
  }
870
859
  else {
871
- tableRow = { type: 'tableRow' } ;
860
+ tableRow = new documentParts.TableRow() ;
872
861
  ctx.parts.push( tableRow ) ;
873
862
  }
874
863
 
@@ -902,7 +891,7 @@ function parseTableRow( str , ctx ) {
902
891
  firstSpace = searchNext( str , currentBar + 1 , nextBar , ' ' ) ;
903
892
  }
904
893
 
905
- let tableCell = { type: 'tableCell' } ;
894
+ let tableCell = new documentParts.TableCell() ;
906
895
 
907
896
  // The '|' bar position helps for column span calculation
908
897
  // sx = Start X, the x position of the left bar
@@ -951,7 +940,7 @@ function parseTableMultilineRow( str , ctx , lastCharOfLine , table , tableRow )
951
940
 
952
941
  if ( str[ nextBar + 1 ] === '|' ) { columnSeparator = true ; }
953
942
 
954
- tableCell = tableRow.children[ columnIndex ] ;
943
+ tableCell = tableRow.parts[ columnIndex ] ;
955
944
 
956
945
  if ( tableCell ) {
957
946
  ctx.i = firstSpace + 1 ;
@@ -975,7 +964,7 @@ function parseTableRowSeparator( str , ctx , thick = false ) {
975
964
  table = ctx.parts[ ctx.parts.length - 1 ] ;
976
965
 
977
966
  if ( ! table || table.type !== 'table' || ctx.lastLineWasEmpty ) {
978
- table = { type: 'table' , columns: [] , children: [] } ;
967
+ table = new documentParts.Table() ;
979
968
  ctx.parts.push( table ) ;
980
969
  }
981
970
 
@@ -989,8 +978,8 @@ function parseTableRowSeparator( str , ctx , thick = false ) {
989
978
  if ( ! table.hasRowSeparator ) {
990
979
  table.multilineRowMode = true ;
991
980
 
992
- for ( let index = 0 ; index < table.children.length ; index ++ ) {
993
- let child = table.children[ index ] ;
981
+ for ( let index = 0 ; index < table.parts.length ; index ++ ) {
982
+ let child = table.parts[ index ] ;
994
983
 
995
984
  if ( child.type === 'tableRow' ) {
996
985
  if ( ! tableRow ) {
@@ -999,22 +988,22 @@ function parseTableRowSeparator( str , ctx , thick = false ) {
999
988
  else {
1000
989
  // All subsequent tableRows, are merged into the first tableRow
1001
990
 
1002
- if ( child.children ) {
1003
- for ( columnIndex = 0 ; columnIndex < child.children.length ; columnIndex ++ ) {
1004
- let child2 = child.children[ columnIndex ] ;
991
+ if ( child.parts ) {
992
+ for ( columnIndex = 0 ; columnIndex < child.parts.length ; columnIndex ++ ) {
993
+ let child2 = child.parts[ columnIndex ] ;
1005
994
  if ( child2.type === 'tableCell' || child2.type === 'tableHeadCell' ) {
1006
- if ( tableRow.children[ columnIndex ] ) {
995
+ if ( tableRow.parts[ columnIndex ] ) {
1007
996
  // Merge the cells
1008
- mergeInlineParts( tableRow.children[ columnIndex ].children , child2.children ) ;
997
+ mergeInlineParts( tableRow.parts[ columnIndex ].parts , child2.parts ) ;
1009
998
  }
1010
999
  else {
1011
- tableRow.children[ columnIndex ] = child2 ;
1000
+ tableRow.parts[ columnIndex ] = child2 ;
1012
1001
  }
1013
1002
  }
1014
1003
  }
1015
1004
  }
1016
1005
 
1017
- table.children.splice( index , 1 ) ;
1006
+ table.parts.splice( index , 1 ) ;
1018
1007
  index -- ;
1019
1008
  }
1020
1009
  }
@@ -1156,19 +1145,24 @@ function parseTableHeadRowSeparator( str , ctx , thick , lastCharOfLine ) {
1156
1145
  // Should come AFTER, because it needs column info
1157
1146
  // Fix previous table row as table head row: turn all existing tableRow into tableHeadRow
1158
1147
 
1159
- for ( let index = 0 ; index < table.children.length ; index ++ ) {
1160
- let child = table.children[ index ] ;
1148
+ for ( let index = 0 ; index < table.parts.length ; index ++ ) {
1149
+ let child = table.parts[ index ] ;
1161
1150
 
1162
1151
  if ( child.type === 'tableRow' ) {
1163
1152
  if ( ! tableHeadRow ) {
1164
1153
  // This is the first tableRow, turn it into a a tableHeadRow
1165
- child.type = 'tableHeadRow' ;
1166
-
1167
- if ( child.children ) {
1168
- for ( columnIndex = 0 ; columnIndex < child.children.length ; columnIndex ++ ) {
1169
- let child2 = child.children[ columnIndex ] ;
1170
- if ( child2.type === 'tableCell' || child2.type === 'tableHeadCell' ) {
1171
- child2.type = 'tableHeadCell' ;
1154
+ //child.type = 'tableHeadRow' ;
1155
+ table.parts[ index ] = child = child.toHead() ;
1156
+
1157
+ if ( child.parts ) {
1158
+ for ( columnIndex = 0 ; columnIndex < child.parts.length ; columnIndex ++ ) {
1159
+ let child2 = child.parts[ columnIndex ] ;
1160
+ if ( child2.type === 'tableCell' ) {
1161
+ //child2.type = 'tableHeadCell' ;
1162
+ child.parts[ columnIndex ] = child2 = child2.toHead() ;
1163
+ child2.isColumnHead = true ;
1164
+ }
1165
+ else if ( child2.type === 'tableHeadCell' ) {
1172
1166
  child2.isColumnHead = true ;
1173
1167
  }
1174
1168
  }
@@ -1180,24 +1174,35 @@ function parseTableHeadRowSeparator( str , ctx , thick , lastCharOfLine ) {
1180
1174
  else {
1181
1175
  // All subsequent tableRows, are merged into the first tableHeadRow created
1182
1176
 
1183
- if ( child.children ) {
1184
- for ( columnIndex = 0 ; columnIndex < child.children.length ; columnIndex ++ ) {
1185
- let child2 = child.children[ columnIndex ] ;
1186
- if ( child2.type === 'tableCell' || child2.type === 'tableHeadCell' ) {
1187
- if ( tableHeadRow.children[ columnIndex ] ) {
1177
+ if ( child.parts ) {
1178
+ for ( columnIndex = 0 ; columnIndex < child.parts.length ; columnIndex ++ ) {
1179
+ let child2 = child.parts[ columnIndex ] ;
1180
+ if ( child2.type === 'tableCell' ) {
1181
+ if ( tableHeadRow.parts[ columnIndex ] ) {
1182
+ // Merge the cells
1183
+ mergeInlineParts( tableHeadRow.parts[ columnIndex ].parts , child2.parts ) ;
1184
+ }
1185
+ else {
1186
+ //child2.type = 'tableHeadCell' ;
1187
+ child.parts[ columnIndex ] = child2 = child2.toHead() ;
1188
+ child2.isColumnHead = true ;
1189
+ tableHeadRow.parts[ columnIndex ] = child2 ;
1190
+ }
1191
+ }
1192
+ else if ( child2.type === 'tableHeadCell' ) {
1193
+ if ( tableHeadRow.parts[ columnIndex ] ) {
1188
1194
  // Merge the cells
1189
- mergeInlineParts( tableHeadRow.children[ columnIndex ].children , child2.children ) ;
1195
+ mergeInlineParts( tableHeadRow.parts[ columnIndex ].parts , child2.parts ) ;
1190
1196
  }
1191
1197
  else {
1192
- child2.type = 'tableHeadCell' ;
1193
1198
  child2.isColumnHead = true ;
1194
- tableHeadRow.children[ columnIndex ] = child2 ;
1199
+ tableHeadRow.parts[ columnIndex ] = child2 ;
1195
1200
  }
1196
1201
  }
1197
1202
  }
1198
1203
  }
1199
1204
 
1200
- table.children.splice( index , 1 ) ;
1205
+ table.parts.splice( index , 1 ) ;
1201
1206
  index -- ;
1202
1207
  }
1203
1208
  }
@@ -1216,11 +1221,11 @@ function parseTableHeadRowSeparator( str , ctx , thick , lastCharOfLine ) {
1216
1221
  function computeIndexColumnSpan( ctx , table , tableRow ) {
1217
1222
  var tableCell , cellIndex , column , columnIndex , columnSpan ,
1218
1223
  columns = table.columns ,
1219
- extraSpan = columns ? columns.length - tableRow.children.length : 0 ;
1224
+ extraSpan = columns ? columns.length - tableRow.parts.length : 0 ;
1220
1225
 
1221
1226
 
1222
- for ( cellIndex = columnIndex = 0 ; cellIndex < tableRow.children.length ; cellIndex ++ , columnIndex ++ ) {
1223
- tableCell = tableRow.children[ cellIndex ] ;
1227
+ for ( cellIndex = columnIndex = 0 ; cellIndex < tableRow.parts.length ; cellIndex ++ , columnIndex ++ ) {
1228
+ tableCell = tableRow.parts[ cellIndex ] ;
1224
1229
  tableCell.column = columnIndex ;
1225
1230
  columnSpan = 1 ;
1226
1231
 
@@ -1229,7 +1234,11 @@ function computeIndexColumnSpan( ctx , table , tableRow ) {
1229
1234
 
1230
1235
  if ( column ) {
1231
1236
  if ( column.headColumn ) {
1232
- tableCell.type = 'tableHeadCell' ;
1237
+ if ( tableCell.type === 'tableCell' ) {
1238
+ //tableCell.type = 'tableHeadCell' ;
1239
+ tableRow.parts[ cellIndex ] = tableCell = tableCell.toHead() ;
1240
+ }
1241
+
1233
1242
  tableCell.isRowHead = true ;
1234
1243
  }
1235
1244
  }
@@ -1308,11 +1317,11 @@ function parseInline( str , ctx , blockEnd , trim = false ) {
1308
1317
  }
1309
1318
  else if ( char === '*' && ! WHITE_SPACES.has( str[ ctx.i + 1 ] ) ) {
1310
1319
  addInlineTextChunk( str , ctx ) ;
1311
- parseEmphasis( str , ctx , scanEnd ) ;
1320
+ parseEmphasisText( str , ctx , scanEnd ) ;
1312
1321
  }
1313
1322
  else if ( char === '_' && ! WHITE_SPACES.has( str[ ctx.i + 1 ] ) ) {
1314
1323
  addInlineTextChunk( str , ctx ) ;
1315
- parseDecoration( str , ctx , scanEnd ) ;
1324
+ parseDecoratedText( str , ctx , scanEnd ) ;
1316
1325
  }
1317
1326
  else if ( char === '`' ) {
1318
1327
  addInlineTextChunk( str , ctx ) ;
@@ -1353,10 +1362,7 @@ function addInlineTextChunk( str , ctx , forcedChunk = null ) {
1353
1362
  lastPart.text += chunk ;
1354
1363
  }
1355
1364
  else {
1356
- ctx.parts.push( {
1357
- type: 'text' ,
1358
- text: chunk
1359
- } ) ;
1365
+ ctx.parts.push( new documentParts.Text( chunk ) ) ;
1360
1366
  }
1361
1367
  }
1362
1368
 
@@ -1414,7 +1420,7 @@ function parseEscape( str , ctx ) {
1414
1420
 
1415
1421
 
1416
1422
 
1417
- function parseEmphasis( str , ctx , blockEnd ) {
1423
+ function parseEmphasisText( str , ctx , blockEnd ) {
1418
1424
  //console.error( "parseStyledText()" ) ;
1419
1425
  var streak = countStreak( str , ctx.i , '*' ) ;
1420
1426
  if ( streak > 3 ) { return ; }
@@ -1423,14 +1429,14 @@ function parseEmphasis( str , ctx , blockEnd ) {
1423
1429
 
1424
1430
  var text = str.slice( ctx.i + streak , end + 1 - streak ) ;
1425
1431
 
1426
- ctx.parts.push( { type: 'emphasis' , level: streak , text } ) ;
1432
+ ctx.parts.push( new documentParts.EmphasisText( text , streak ) ) ;
1427
1433
  ctx.i = end ;
1428
1434
  ctx.iStartOfInlineChunk = ctx.i + 1 ;
1429
1435
  }
1430
1436
 
1431
1437
 
1432
1438
 
1433
- function parseDecoration( str , ctx , blockEnd ) {
1439
+ function parseDecoratedText( str , ctx , blockEnd ) {
1434
1440
  //console.error( "parseStyledText()" ) ;
1435
1441
  var streak = countStreak( str , ctx.i , '_' ) ;
1436
1442
  if ( streak > 2 ) { return ; }
@@ -1439,9 +1445,7 @@ function parseDecoration( str , ctx , blockEnd ) {
1439
1445
 
1440
1446
  var text = str.slice( ctx.i + streak , end + 1 - streak ) ;
1441
1447
 
1442
- ctx.parts.push( {
1443
- type: 'decoration' , underline: true , level: streak , text
1444
- } ) ;
1448
+ ctx.parts.push( new documentParts.DecoratedText( text , streak ) ) ;
1445
1449
  ctx.i = end ;
1446
1450
  ctx.iStartOfInlineChunk = ctx.i + 1 ;
1447
1451
  }
@@ -1464,7 +1468,7 @@ function parseCode( str , ctx , blockEnd ) {
1464
1468
 
1465
1469
  var text = str.slice( sliceStart , sliceEnd ) ;
1466
1470
 
1467
- ctx.parts.push( { type: 'code' , text } ) ;
1471
+ ctx.parts.push( new documentParts.Code( text ) ) ;
1468
1472
  ctx.i = end ;
1469
1473
  ctx.iStartOfInlineChunk = ctx.i + 1 ;
1470
1474
  }
@@ -1486,18 +1490,15 @@ function parseStyledText( str , ctx , blockEnd ) {
1486
1490
  var data = parseDataMark( str , ctx , STYLE_DATA_MARK , blockEnd ) ;
1487
1491
  if ( ! data ) { return ; }
1488
1492
 
1489
- var params = { type: '' , text } ;
1490
- if ( data.href?.[ 0 ] ) { params.href = data.href[ 0 ] ; }
1491
- if ( data.style?.[ 0 ] ) { params.style = data.style[ 0 ] ; }
1492
- if ( data.text?.[ 0 ] ) { params.title = data.text[ 0 ] ; }
1493
+ var href = data.href?.[ 0 ] ,
1494
+ style = data.style?.[ 0 ] ,
1495
+ title = data.text?.[ 0 ] ;
1493
1496
 
1494
- if ( params.href ) {
1495
- params.type = 'link' ;
1496
- ctx.parts.push( params ) ;
1497
+ if ( href ) {
1498
+ ctx.parts.push( new documentParts.Link( text , href , style , title ) ) ;
1497
1499
  }
1498
- else if ( params.style || params.title ) {
1499
- params.type = 'styledText' ;
1500
- ctx.parts.push( params ) ;
1500
+ else if ( style || title ) {
1501
+ ctx.parts.push( new documentParts.StyledText( text , style , title ) ) ;
1501
1502
  }
1502
1503
  }
1503
1504
 
@@ -1519,29 +1520,13 @@ function parseImage( str , ctx , blockEnd ) {
1519
1520
  var data = parseDataMark( str , ctx , IMAGE_DATA_MARK , blockEnd ) ;
1520
1521
  if ( ! data ) { return ; }
1521
1522
 
1522
- var params = { type: '' , altText: text } ;
1523
- if ( data.href?.[ 0 ] ) { params.href = data.href[ 0 ] ; }
1523
+ var href = data.href?.[ 0 ] ;
1524
1524
 
1525
- if ( params.href ) {
1526
- params.type = 'image' ;
1527
- params.altText = text ;
1528
- if ( data.text?.[ 0 ] ) { params.title = data.text[ 0 ] ; }
1529
- ctx.parts.push( params ) ;
1525
+ if ( href ) {
1526
+ ctx.parts.push( new documentParts.Image( href , text , data.text?.[ 0 ] ) ) ;
1530
1527
  }
1531
1528
  else {
1532
- params.type = 'pictogram' ;
1533
- params.code = text ;
1534
- let emojiChar = emoji.get( text ) ;
1535
- if ( emojiChar ) { params.emoji = emojiChar ; }
1536
-
1537
- if ( data.text?.[ 0 ] ) { params.altText = data.text[ 0 ] ; }
1538
- if ( data.text?.[ 1 ] ) { params.title = data.text[ 1 ] ; }
1539
-
1540
- if ( emojiChar && ! params.altText ) {
1541
- params.altText = emoji.getCanonicalName( emojiChar ) ;
1542
- }
1543
-
1544
- ctx.parts.push( params ) ;
1529
+ ctx.parts.push( new documentParts.Pictogram( text , data.text?.[ 0 ] , data.text?.[ 1 ] ) ) ;
1545
1530
  }
1546
1531
  }
1547
1532
 
@@ -1603,7 +1588,7 @@ function postProcessTableRowSpan( table ) {
1603
1588
  var tableRow , tableCell , masterCell , lastRow , lastContinueRowSpan ;
1604
1589
 
1605
1590
  // First pass: merge cells
1606
- for ( tableRow of table.children ) {
1591
+ for ( tableRow of table.parts ) {
1607
1592
  if ( tableRow.type !== 'tableRow' ) { continue ; }
1608
1593
 
1609
1594
  if ( lastContinueRowSpan ) {
@@ -1612,7 +1597,7 @@ function postProcessTableRowSpan( table ) {
1612
1597
  masterCell = searchColumn( lastRow , columnIndex ) ;
1613
1598
  if ( tableCell && masterCell ) {
1614
1599
  if ( masterCell.masterCell ) { masterCell = masterCell.masterCell ; }
1615
- mergeInlineParts( masterCell.children , tableCell.children ) ;
1600
+ mergeInlineParts( masterCell.parts , tableCell.parts ) ;
1616
1601
  masterCell.rowSpan = ( masterCell.rowSpan || 1 ) + 1 ;
1617
1602
  tableCell.masterCell = masterCell ;
1618
1603
  }
@@ -1624,9 +1609,9 @@ function postProcessTableRowSpan( table ) {
1624
1609
  }
1625
1610
 
1626
1611
  // Second pass: remove dead cells
1627
- for ( tableRow of table.children ) {
1612
+ for ( tableRow of table.parts ) {
1628
1613
  if ( tableRow.type !== 'tableRow' ) { continue ; }
1629
- inPlaceFilter( tableRow.children , tableCell_ => ! tableCell_.masterCell ) ;
1614
+ inPlaceFilter( tableRow.parts , tableCell_ => ! tableCell_.masterCell ) ;
1630
1615
  }
1631
1616
  }
1632
1617
 
@@ -1664,7 +1649,7 @@ function mergeInlineParts( parts , extraParts ) {
1664
1649
  }
1665
1650
  else {
1666
1651
  // Add an additional joint part
1667
- parts.push( { type: 'text' , text: ' ' } ) ;
1652
+ parts.push( new documentParts.Text( ' ' ) ) ;
1668
1653
  }
1669
1654
  }
1670
1655
 
@@ -1897,11 +1882,11 @@ function countStreak( str , i , streaker ) {
1897
1882
 
1898
1883
 
1899
1884
  function searchChildOfType( parent , type ) {
1900
- var children = parent.children ;
1901
- if ( ! children ) { return null ; }
1885
+ var parts = parent.parts ;
1886
+ if ( ! parts ) { return null ; }
1902
1887
 
1903
- for ( let i = 0 ; i < children.length ; i ++ ) {
1904
- let child = children[ i ] ;
1888
+ for ( let i = 0 ; i < parts.length ; i ++ ) {
1889
+ let child = parts[ i ] ;
1905
1890
  if ( child.type === type ) { return child ; }
1906
1891
  }
1907
1892
 
@@ -1911,11 +1896,11 @@ function searchChildOfType( parent , type ) {
1911
1896
 
1912
1897
 
1913
1898
  function searchLastChildOfType( parent , type ) {
1914
- var children = parent.children ;
1915
- if ( ! children ) { return null ; }
1899
+ var parts = parent.parts ;
1900
+ if ( ! parts ) { return null ; }
1916
1901
 
1917
- for ( let i = children.length - 1 ; i >= 0 ; i -- ) {
1918
- let child = children[ i ] ;
1902
+ for ( let i = parts.length - 1 ; i >= 0 ; i -- ) {
1903
+ let child = parts[ i ] ;
1919
1904
  if ( child.type === type ) { return child ; }
1920
1905
  }
1921
1906
 
@@ -1925,7 +1910,7 @@ function searchLastChildOfType( parent , type ) {
1925
1910
 
1926
1911
 
1927
1912
  function searchColumn( tableRow , column ) {
1928
- for ( let tableCell of tableRow.children ) {
1913
+ for ( let tableCell of tableRow.parts ) {
1929
1914
  if ( tableCell.column === column ) { return tableCell ; }
1930
1915
  }
1931
1916
 
@@ -1942,7 +1927,7 @@ function stack( ctx , parent = ctx.parts[ ctx.parts.length - 1 ] ) {
1942
1927
  } ) ;
1943
1928
 
1944
1929
  ctx.parent = parent ;
1945
- ctx.parts = parent.children = parent.children || [] ;
1930
+ ctx.parts = parent.parts = parent.parts || [] ;
1946
1931
  }
1947
1932
 
1948
1933