book-source 0.3.11 → 0.3.13

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.
@@ -30,7 +30,6 @@
30
30
 
31
31
  const documentParts = require( './documentParts.js' ) ;
32
32
  const Style = require( './Style.js' ) ;
33
- const Theme = require( './Theme.js' ) ;
34
33
  const textPostFilters = require( './textPostFilters.js' ) ;
35
34
 
36
35
  const inPlaceFilter = require( 'array-kit/lib/inPlaceFilter.js' ) ;
@@ -38,24 +37,41 @@ const inPlaceFilter = require( 'array-kit/lib/inPlaceFilter.js' ) ;
38
37
 
39
38
 
40
39
  function StructuredDocument() {
40
+ this.type = 'document' ;
41
41
  this.title = 'Document' ;
42
42
  this.metadata = null ;
43
43
  this.theme = null ;
44
+ this.summary = null ;
44
45
  this.parts = [] ;
46
+ //this.idList = new Set() ;
45
47
  }
46
48
 
47
49
  module.exports = StructuredDocument ;
48
50
 
51
+ // Circular require, should come after exporting
52
+ const postProcesses = require( './postProcesses.js' ) ;
49
53
 
50
54
 
51
- StructuredDocument.prototype.render = function( renderer ) {
52
- var meta = {
53
- title: this.title
54
- } ;
55
55
 
56
- var output = this.renderParts( renderer , this.parts , [] , [ {} ] ) ;
56
+ StructuredDocument.prototype.render = function( renderer , special = null ) {
57
+ let toRender ;
58
+
59
+ switch ( special ) {
60
+ case 'summary' :
61
+ toRender = this.summary ? [ this.summary ] : [] ;
62
+ break ;
63
+ default :
64
+ toRender = this.parts ;
65
+ break ;
66
+ }
67
+
68
+ let output = this.renderParts( renderer , toRender , [] , [ {} ] ) ;
57
69
 
58
70
  if ( renderer.document ) {
71
+ let meta = {
72
+ title: this.title
73
+ } ;
74
+
59
75
  output = renderer.document( meta , output ) ;
60
76
  }
61
77
 
@@ -199,25 +215,79 @@ StructuredDocument.prototype.autoTitle = function() {
199
215
 
200
216
  if ( ! header ) { return ; }
201
217
 
202
- str = StructuredDocument.getText( header.parts ) ;
218
+ str = StructuredDocument.getText( header ) ;
203
219
  if ( str ) { this.title = str ; }
204
220
  } ;
205
221
 
206
222
 
207
223
 
208
- StructuredDocument.getText = function( parts ) {
209
- var str = '' ;
224
+ StructuredDocument.prototype.postProcess = function( params = {} ) {
225
+ var data = {} ,
226
+ postProcessArray = [] ,
227
+ postProcessMap = new Map() ;
210
228
 
211
- for ( let part of parts ) {
212
- if ( part.text ) { str += part.text ; }
213
- else if ( part.parts?.length ) { str += StructuredDocument.getText( part.parts ) ; }
229
+ for ( let postProcessName of Object.keys( params ) ) {
230
+ if ( ! postProcesses[ postProcessName ] || ! params[ postProcessName ] ) { continue ; }
231
+
232
+ let postProcess = postProcesses[ postProcessName ] ;
233
+
234
+ // Add required post-process first
235
+ if ( postProcess.require ) {
236
+ for ( let requiredName of postProcess.require ) {
237
+ if ( ! postProcessMap.has( requiredName ) ) {
238
+ let entry = { name: requiredName , params: {} } ;
239
+ postProcessArray.push( entry ) ;
240
+ postProcessMap.set( requiredName , entry ) ;
241
+ }
242
+ }
243
+ }
244
+
245
+ if ( postProcessMap.has( postProcessName ) ) {
246
+ // It was already added by a dependency, just patch the params if needed
247
+ if ( typeof params[ postProcessName ] === 'object' ) {
248
+ let entry = postProcessMap.get( postProcessName ) ;
249
+ entry.params = Object.assign( {} , params[ postProcessName ] ) ;
250
+ }
251
+ }
252
+ else {
253
+ let entry = {
254
+ name: postProcessName ,
255
+ params: typeof params[ postProcessName ] === 'object' ? Object.assign( {} , params[ postProcessName ] ) : {}
256
+ } ;
257
+
258
+ postProcessArray.push( entry ) ;
259
+ postProcessMap.set( postProcessName , entry ) ;
260
+ }
214
261
  }
215
262
 
216
- return str ;
263
+ for ( let entry of postProcessArray ) {
264
+ if ( postProcesses[ entry.name ].init ) {
265
+ postProcesses[ entry.name ].init( this , entry.params , data ) ;
266
+ }
267
+ }
268
+
269
+ this.postProcessRecursive( this , postProcessArray , data ) ;
270
+
271
+ for ( let entry of postProcessArray ) {
272
+ if ( postProcesses[ entry.name ].finalize ) {
273
+ postProcesses[ entry.name ].finalize( this , entry.params , data ) ;
274
+ }
275
+ }
217
276
  } ;
218
277
 
219
- // Backqard compatibility/API
220
- StructuredDocument.prototype.getText = function( parts = this.parts ) { return StructuredDocument.getText( parts ) ; } ;
278
+
279
+
280
+ StructuredDocument.prototype.postProcessRecursive = function( object , postProcessArray , data ) {
281
+ for ( let entry of postProcessArray ) {
282
+ postProcesses[ entry.name ]( this , object , entry.params , data ) ;
283
+ }
284
+
285
+ if ( object.parts && object.parts.length ) {
286
+ for ( let part of object.parts ) {
287
+ this.postProcessRecursive( part , postProcessArray , data ) ;
288
+ }
289
+ }
290
+ } ;
221
291
 
222
292
 
223
293
 
@@ -251,10 +321,49 @@ StructuredDocument.prototype.oneTextPostFilter = function( postFilter , parts ,
251
321
 
252
322
 
253
323
 
324
+ // Utility methods
325
+
326
+
327
+
328
+ StructuredDocument.getText = function( object ) {
329
+ if ( Array.isArray( object ) ) {
330
+ object = { parts: object } ;
331
+ }
332
+
333
+ if ( object.text ) { return object.text ; }
334
+ if ( ! object.parts || ! object.parts.length ) { return '' ; }
335
+
336
+ let str = '' ;
337
+
338
+ for ( let part of object.parts ) {
339
+ str += StructuredDocument.getText( part ) ;
340
+ }
341
+
342
+ return str ;
343
+ } ;
344
+
345
+ // Backward compatibility/API
346
+ StructuredDocument.prototype.getText = function( object = this ) { return StructuredDocument.getText( object ) ; } ;
347
+
348
+
349
+
350
+ StructuredDocument.textToId = function( text ) {
351
+ return text
352
+ .trim()
353
+ .normalize( 'NFC' )
354
+ .replace( /[\s]+/g , '_' )
355
+ .replace( /[^\p{L}\p{N}_.:-]/gu , '' )
356
+ .replace( /_+/g , '_' ) ;
357
+ } ;
358
+
359
+
360
+
254
361
  // Parser
255
362
 
256
- StructuredDocument.parse = function( str , options ) {
257
- if ( ! options || typeof options !== 'object' ) { options = {} ; }
363
+
364
+
365
+ StructuredDocument.parse = function( str , params ) {
366
+ if ( ! params || typeof params !== 'object' ) { params = {} ; }
258
367
 
259
368
  if ( typeof str !== 'string' ) {
260
369
  if ( str && typeof str === 'object' ) { str = str.toString() ; }
@@ -281,10 +390,11 @@ StructuredDocument.parse = function( str , options ) {
281
390
  parseBlocks( str , ctx ) ;
282
391
 
283
392
  for ( let table of ctx.rowSpanTables ) { postProcessTableRowSpan( table ) ; }
393
+
284
394
  ctx.structuredDocument.autoTitle() ;
285
395
 
286
396
  if ( ctx.rawMetadata ) {
287
- let metadataParser = options.metadataParser || JSON.parse ;
397
+ let metadataParser = params.metadataParser || JSON.parse ;
288
398
  for ( let type in ctx.rawMetadata ) {
289
399
  try {
290
400
  let parsed = metadataParser( ctx.rawMetadata[ type ] ) ;
@@ -387,6 +497,9 @@ function parseBlock( str , ctx ) {
387
497
  case BLOCK_METADATA :
388
498
  parseMetadata( str , ctx ) ;
389
499
  break ;
500
+ case BLOCK_SPECIAL :
501
+ parseSpecial( str , ctx ) ;
502
+ break ;
390
503
  default :
391
504
  throw new Error( "Bad block detection: " + blockType ) ;
392
505
  }
@@ -495,6 +608,7 @@ const BLOCK_TABLE_ROW_THICK_SEPARATOR = 42 ;
495
608
  const BLOCK_TABLE_CAPTION = 43 ;
496
609
  const BLOCK_ANCHOR = 50 ;
497
610
  const BLOCK_METADATA = 60 ;
611
+ const BLOCK_SPECIAL = 80 ;
498
612
 
499
613
  function detectBlockType( str , i ) {
500
614
  if ( str[ i ] === '\\' ) {
@@ -502,14 +616,17 @@ function detectBlockType( str , i ) {
502
616
  }
503
617
 
504
618
  if ( str[ i ] === '#' ) {
505
- if ( str[ i + 1 ] === '(' ) { return BLOCK_ANCHOR ; }
619
+ if ( str[ i + 1 ] === '(' ) {
620
+ if ( str[ i + 2 ] === '%' ) { return BLOCK_SPECIAL ; }
621
+ return BLOCK_ANCHOR ;
622
+ }
506
623
  return BLOCK_HEADER ;
507
624
  }
508
625
 
509
626
  if ( str[ i ] === '!' && str[ i + 2 ] === '[' ) {
510
627
  if ( str[ i + 1 ] === '=' ) { return BLOCK_MEDIA ; }
511
- else if ( str[ i + 1 ] === '<' ) { return BLOCK_FLOAT_LEFT_MEDIA ; }
512
- else if ( str[ i + 1 ] === '>' ) { return BLOCK_FLOAT_RIGHT_MEDIA ; }
628
+ if ( str[ i + 1 ] === '<' ) { return BLOCK_FLOAT_LEFT_MEDIA ; }
629
+ if ( str[ i + 1 ] === '>' ) { return BLOCK_FLOAT_RIGHT_MEDIA ; }
513
630
  return BLOCK_PARAGRAPH ;
514
631
  }
515
632
 
@@ -860,6 +977,18 @@ function parseAnchor( str , ctx ) {
860
977
 
861
978
 
862
979
 
980
+ function parseSpecial( str , ctx ) {
981
+ //console.log( "parseSpecial()" ) ;
982
+ var end = searchCloser( str , ctx.i + 3 , '(' , ')' , true ) ;
983
+ if ( end < 0 ) { return parseParagraph( str , ctx ) ; }
984
+
985
+ ctx.parts.push( new documentParts.Special( str.slice( ctx.i + 3 , end ) ) ) ;
986
+
987
+ ctx.i = end + 1 ;
988
+ }
989
+
990
+
991
+
863
992
  function parseTableCaption( str , ctx ) {
864
993
  //console.log( "parseTableCaption()" ) ;
865
994
  var lastCharOfLine = searchLastCharOfLine( str , ctx.i + 1 ) ;
@@ -1369,9 +1498,7 @@ function parseInlineChildrenOfParent( str , ctx , parent , blockEnd , trim = fal
1369
1498
  // Try to parse non-block content
1370
1499
  function parseInline( str , ctx , blockEnd , trim = false ) {
1371
1500
  //console.log( "parseInline() -- remaining:" , ctx.i , str.slice( ctx.i ) ) ;
1372
- var isSpace , scanEnd ;
1373
-
1374
- scanEnd = blockEnd = blockEnd ?? searchEndOfLine( str , ctx.i ) ;
1501
+ var scanEnd = blockEnd = blockEnd ?? searchEndOfLine( str , ctx.i ) ;
1375
1502
 
1376
1503
  if ( trim ) {
1377
1504
  let first = searchNextNotInSet( str , ctx.i , blockEnd , WHITE_SPACES ) ;
@@ -1657,7 +1784,7 @@ function parseInfotipedText( str , ctx , scanEnd ) {
1657
1784
  ctx.iStartOfInlineChunk = ctx.i + 1 ;
1658
1785
 
1659
1786
  if ( ! href && ! hint ) {
1660
- infotipedText.href = StructuredDocument.getText( infotipedText.parts ) ;
1787
+ infotipedText.href = StructuredDocument.getText( infotipedText ) ;
1661
1788
  }
1662
1789
  }
1663
1790
 
@@ -2011,7 +2138,7 @@ function searchBlockSwitchCloser( str , i , closer , closerMinStreak = 1 , end =
2011
2138
  Same that searchBlockSwitchCloser() but with a callback function.
2012
2139
  */
2013
2140
  function searchFixedBlockSwitchCloser( str , i , fixed , end = str.length ) {
2014
- var test , j , failed ;
2141
+ var j , failed ;
2015
2142
 
2016
2143
  while ( i < end ) {
2017
2144
  // Search next line
@@ -33,13 +33,17 @@ module.exports = documentParts ;
33
33
 
34
34
 
35
35
 
36
- function Part() {}
36
+ function Part() {
37
+ this.id = null ;
38
+ }
37
39
 
38
40
  documentParts.Part = Part ;
39
41
 
40
42
 
41
43
 
42
- function InlinePart() {}
44
+ function InlinePart() {
45
+ Part.call( this ) ;
46
+ }
43
47
 
44
48
  InlinePart.prototype = Object.create( Part.prototype ) ;
45
49
  InlinePart.prototype.constructor = InlinePart ;
@@ -48,6 +52,7 @@ documentParts.InlinePart = InlinePart ;
48
52
 
49
53
 
50
54
  function InlineContainerPart() {
55
+ Part.call( this ) ;
51
56
  this.parts = [] ;
52
57
  }
53
58
 
@@ -58,6 +63,7 @@ documentParts.InlineContainerPart = InlineContainerPart ;
58
63
 
59
64
 
60
65
  function InlineTextPart( text ) {
66
+ Part.call( this ) ;
61
67
  this.text = text ;
62
68
  }
63
69
 
@@ -67,7 +73,9 @@ documentParts.InlineTextPart = InlineTextPart ;
67
73
 
68
74
 
69
75
 
70
- function BlockPart() {}
76
+ function BlockPart() {
77
+ Part.call( this ) ;
78
+ }
71
79
 
72
80
  BlockPart.prototype = Object.create( Part.prototype ) ;
73
81
  BlockPart.prototype.constructor = BlockPart ;
@@ -76,6 +84,7 @@ documentParts.BlockPart = BlockPart ;
76
84
 
77
85
 
78
86
  function BlockContainerPart() {
87
+ Part.call( this ) ;
79
88
  this.parts = [] ;
80
89
  }
81
90
 
@@ -362,6 +371,18 @@ documentParts.Anchor = Anchor ;
362
371
 
363
372
 
364
373
 
374
+ function Special( special ) {
375
+ this.type = 'special' ;
376
+ this.special = special ;
377
+ BlockContainerPart.call( this ) ;
378
+ }
379
+
380
+ Special.prototype = Object.create( BlockContainerPart.prototype ) ;
381
+ Special.prototype.constructor = Special ;
382
+ documentParts.Special = Special ;
383
+
384
+
385
+
365
386
  function ImageBlock( href , altText , float , caption , hint ) {
366
387
  this.type = 'imageBlock' ;
367
388
  this.href = href ;
@@ -0,0 +1,150 @@
1
+ /*
2
+ Book Source
3
+
4
+ Copyright (c) 2023 Cédric Ronvel
5
+
6
+ The MIT License (MIT)
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
25
+ */
26
+
27
+ "use strict" ;
28
+
29
+
30
+
31
+ const StructuredDocument = require( './StructuredDocument.js' ) ;
32
+ const documentParts = require( './documentParts.js' ) ;
33
+
34
+
35
+
36
+ exports.autoId = ( structuredDocument , object , params , data ) => {
37
+ if ( ! params.types.has( object.type ) ) { return ; }
38
+
39
+ let text = StructuredDocument.getText( object ) ;
40
+ let id = data.idPrefix + StructuredDocument.textToId( text ) ;
41
+
42
+ if ( data.idList.has( id ) ) {
43
+ let originalId = id ;
44
+ let count = data.idList.get( originalId ) ;
45
+ count ++ ;
46
+ id = originalId + '_' + count ;
47
+
48
+ while ( data.idList.has( id ) ) {
49
+ count ++ ;
50
+ id = originalId + '_' + count ;
51
+ }
52
+
53
+ data.idList.set( originalId , count ) ;
54
+ }
55
+
56
+ // Always add the current ID, even if originalId was added too
57
+ data.idList.set( id , 1 ) ;
58
+
59
+ object.id = id ;
60
+ } ;
61
+
62
+ const DEFAULT_AUTO_ID_TYPES = new Set( [ 'header' ] ) ;
63
+
64
+ exports.autoId.init = ( structuredDocument , params , data ) => {
65
+ if ( Array.isArray( params.types ) ) { params.types = new Set( params.types ) ; }
66
+ else if ( ! params.types || ! ( params.types instanceof Set ) ) { params.types = DEFAULT_AUTO_ID_TYPES ; }
67
+
68
+ if ( data.idPrefix === undefined ) { data.idPrefix = params.idPrefix || '' ; }
69
+
70
+ data.idList = new Map() ;
71
+ } ;
72
+
73
+
74
+
75
+ exports.summary = ( structuredDocument , object , params , data ) => {
76
+ if ( object.type === 'special' ) {
77
+ switch ( object.special ) {
78
+ case 'summary' :
79
+ data.summaryObjects.push( object ) ;
80
+ break ;
81
+ case 'summary-start' :
82
+ resetSummary( data ) ;
83
+ break ;
84
+ case 'summary-end' :
85
+ data.summaryEnded = true ;
86
+ break ;
87
+ }
88
+
89
+ return ;
90
+ }
91
+
92
+ if ( data.summaryEnded || object.type !== 'header' || object.level > params.maxLevel ) { return ; }
93
+
94
+ let item = new documentParts.ListItem( object.level - 1 ) ;
95
+ let link = new documentParts.Link( '#' + object.id ) ;
96
+ item.parts.push( link ) ;
97
+ let text = new documentParts.Text( StructuredDocument.getText( object ) ) ;
98
+ link.parts.push( text ) ;
99
+
100
+ if ( ! data.summaryHeaderLevelStack.length ) {
101
+ data.summaryHeaderLevelStack.push( object.level ) ;
102
+ }
103
+
104
+ let lastListLevel = data.summaryHeaderLevelStack.length - 1 ;
105
+ let lastHeaderLevel = data.summaryHeaderLevelStack[ lastListLevel ] ;
106
+
107
+ while ( object.level < lastHeaderLevel && lastListLevel > 0 ) {
108
+ lastListLevel -- ;
109
+ lastHeaderLevel = data.summaryHeaderLevelStack[ lastListLevel ] ;
110
+ data.summaryHeaderLevelStack.length = data.summaryListStack.length = lastListLevel + 1 ;
111
+ }
112
+
113
+ if ( object.level > lastHeaderLevel ) {
114
+ let parentList = data.summaryListStack[ lastListLevel ] ;
115
+ let currentList = new documentParts.List( object.level - 1 ) ;
116
+ data.summaryListStack.push( currentList ) ;
117
+ data.summaryHeaderLevelStack.push( object.level ) ;
118
+ currentList.parts.push( item ) ;
119
+ parentList.parts.push( currentList ) ;
120
+ }
121
+ else {
122
+ // It's equal OR the doc is not well-formed and it's less, nevertheless, we just act as if it's equal
123
+ let currentList = data.summaryListStack[ lastListLevel ] ;
124
+ currentList.parts.push( item ) ;
125
+ }
126
+ } ;
127
+
128
+ exports.summary.init = ( structuredDocument , params , data ) => {
129
+ params.maxLevel = params.maxLevel || Infinity ;
130
+ data.summaryObjects = [] ;
131
+ resetSummary( data ) ;
132
+ } ;
133
+
134
+ function resetSummary( data ) {
135
+ data.summaryList = new documentParts.List( 0 ) ;
136
+ data.summaryListStack = [ data.summaryList ] ;
137
+ data.summaryHeaderLevelStack = [] ;
138
+ data.summaryEnded = false ;
139
+ }
140
+
141
+ exports.summary.finalize = ( structuredDocument , params , data ) => {
142
+ structuredDocument.summary = data.summaryList ;
143
+
144
+ for ( let summaryObject of data.summaryObjects ) {
145
+ summaryObject.parts.push( data.summaryList ) ;
146
+ }
147
+ } ;
148
+
149
+ exports.summary.require = [ 'autoId' ] ;
150
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "book-source",
3
- "version": "0.3.11",
3
+ "version": "0.3.13",
4
4
  "description": "A lightweight markup language, inspired by Markdown.",
5
5
  "main": "lib/book-source.js",
6
6
  "directories": {