docgen-tool 2.1.2 → 3.0.0

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.
Files changed (81) hide show
  1. package/.gitattributes +1 -0
  2. package/.github/workflows/deploy-website.yml +28 -0
  3. package/.prettierignore +4 -0
  4. package/.prettierrc +4 -0
  5. package/LICENSE +35 -33
  6. package/{docgen → dist/docgen.js} +11 -22
  7. package/docgen.js +95 -0
  8. package/package.json +47 -20
  9. package/source/__test__/test-run/contents.json +12 -0
  10. package/source/__test__/test-run/overview.txt +3 -0
  11. package/source/__test__/test-run/parameters.json +32 -0
  12. package/source/__test__/test-run/release-notes.txt +4 -0
  13. package/source/docgen.js +900 -775
  14. package/source/example/contents.json +11 -11
  15. package/source/example/index.txt +3 -3
  16. package/source/example/parameters.json +36 -36
  17. package/source/internal-readme.md +4 -0
  18. package/source/pdf-stylesheet.css +26 -20
  19. package/source/require/docgen.css +76 -69
  20. package/source/require/katexInjector.js +9 -13
  21. package/source/require/print.css +26 -19
  22. package/source/templates/main.html +93 -93
  23. package/source/user-guide/advanced-content.txt +170 -170
  24. package/source/user-guide/contents.json +56 -56
  25. package/source/user-guide/files/images/svg/inkit-logo.svg +9 -0
  26. package/source/user-guide/index.txt +257 -244
  27. package/source/user-guide/installation.txt +49 -49
  28. package/source/user-guide/parameters.json +36 -36
  29. package/source/user-guide/release-notes.txt +69 -58
  30. package/source/user-guide/running.txt +46 -46
  31. package/source/user-guide/troubleshooting.txt +31 -31
  32. package/source/user-guide/upgrading.txt +25 -25
  33. package/source/user-guide/version-control.txt +13 -13
  34. package/source/user-guide/writing-content.txt +269 -269
  35. package/tsconfig.json +8 -0
  36. package/.npmignore +0 -2
  37. package/docs/advanced-content.html +0 -239
  38. package/docs/commonmark.html +0 -225
  39. package/docs/docgen.pdf +0 -0
  40. package/docs/files/images/overview.png +0 -0
  41. package/docs/files/images/pdf.png +0 -0
  42. package/docs/files/images/svg/icon.svg +0 -845
  43. package/docs/files/images/svg/overview.svg +0 -1345
  44. package/docs/files/images/text.png +0 -0
  45. package/docs/files/images/web.png +0 -0
  46. package/docs/index.html +0 -333
  47. package/docs/installation.html +0 -121
  48. package/docs/ownership.html +0 -164
  49. package/docs/release-notes.html +0 -144
  50. package/docs/require/docgen.css +0 -131
  51. package/docs/require/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
  52. package/docs/require/katex/fonts/KaTeX_Main-Bold.woff +0 -0
  53. package/docs/require/katex/fonts/KaTeX_Main-Italic.woff +0 -0
  54. package/docs/require/katex/fonts/KaTeX_Main-Regular.woff +0 -0
  55. package/docs/require/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  56. package/docs/require/katex/fonts/KaTeX_Math-Italic.woff +0 -0
  57. package/docs/require/katex/fonts/KaTeX_Math-Regular.woff +0 -0
  58. package/docs/require/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
  59. package/docs/require/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
  60. package/docs/require/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
  61. package/docs/require/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
  62. package/docs/require/katex/katex.min.css +0 -1
  63. package/docs/require/katex/katex.min.js +0 -6
  64. package/docs/require/katexInjector.js +0 -22
  65. package/docs/require/print.css +0 -19
  66. package/docs/require/webknife/fonts/DroidSansMono.woff +0 -0
  67. package/docs/require/webknife/fonts/OpenSans.woff +0 -0
  68. package/docs/require/webknife/fonts/OpenSansBold.woff +0 -0
  69. package/docs/require/webknife/fonts/OpenSansBoldItalic.woff +0 -0
  70. package/docs/require/webknife/fonts/OpenSansItalic.woff +0 -0
  71. package/docs/require/webknife/framework.icons.js +0 -82
  72. package/docs/require/webknife/framework.min.css +0 -3
  73. package/docs/require/webknife/framework.min.js +0 -14
  74. package/docs/require/webknife/highlight.min.css +0 -1
  75. package/docs/require/webknife/highlight.min.js +0 -6
  76. package/docs/running.html +0 -123
  77. package/docs/troubleshooting.html +0 -105
  78. package/docs/upgrading.html +0 -124
  79. package/docs/version-control.html +0 -102
  80. package/docs/writing-content.html +0 -305
  81. /package/{docs → source/__test__/test-run}/files/images/logo.png +0 -0
package/source/docgen.js CHANGED
@@ -1,863 +1,988 @@
1
-
2
- var rsvp = require('rsvp');
3
- var fs = require('fs-extra');
4
- var path = require('path');
5
- var cheerio = require('cheerio');
6
- var markdown = require('markdown-it')('commonmark').enable('table');;
7
- var moment = require('moment');
8
- var childProcess = require("child_process");
9
- var schemaValidator = require("z-schema");
10
- var chalk = require('chalk');
11
- var spawnArgs = require('spawn-args');
12
- var cliSpinner = require('cli-spinner').Spinner;
13
- var imageSizeOf = require('image-size');
1
+ const rsvp = require('rsvp');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const cheerio = require('cheerio');
5
+ const markdown = require('markdown-it')('commonmark').enable('table');
6
+ const moment = require('moment');
7
+ import { spawn, exec } from 'child_process';
8
+ const schemaValidator = require('z-schema');
9
+ const chalk = require('chalk');
10
+ const spawnArgs = require('spawn-args');
11
+ const cliSpinner = require('cli-spinner').Spinner;
12
+ const imageSizeOf = require('image-size');
13
+
14
+ //Allow CommonMark links that use other protocols, such as file:///
15
+ //The markdown-it implementation is more restrictive than the CommonMark spec
16
+ //See https://github.com/markdown-it/markdown-it/issues/108
17
+ markdown.validateLink = () => {
18
+ return true;
19
+ };
14
20
 
15
21
  /**
16
- * DocGen class
17
- */
18
-
19
- function DocGen (process)
20
- {
21
- var mainProcess = process;
22
- var version = '2.1.2';
23
- var wkhtmltopdfVersion = 'wkhtmltopdf 0.12.2.1 (with patched qt)'; //output from wkhtmltopdf -V
24
- var options;
25
- var templates = {};
26
- var meta = {};
27
- var pages = {};
28
- var sortedPages = {};
29
-
30
- this.getVersion = function () {
31
- return version;
22
+ * DocGen class
23
+ */
24
+
25
+ function DocGen(process) {
26
+ let mainProcess = process;
27
+ let version = '3.0.0';
28
+ let wkhtmltopdfVersion = 'wkhtmltopdf 0.12.6 (with patched qt)'; //output from wkhtmltopdf -V
29
+ let options;
30
+ let templates = {};
31
+ let meta = {};
32
+ let pages = {};
33
+ let sortedPages = {};
34
+
35
+ this.getVersion = () => {
36
+ return version;
37
+ };
38
+
39
+ this.setOptions = (userOptions) => {
40
+ options = userOptions;
41
+ //all user-specified paths must be normalized
42
+ if (options.input) {
43
+ options.input = path.normalize(options.input + '/');
44
+ }
45
+ if (options.output) {
46
+ options.output = path.normalize(options.output + '/');
32
47
  }
33
48
 
34
- this.setOptions = function (userOptions) {
35
- options = userOptions;
36
- //all user-specified paths must be normalized
37
- if (options.input) {
38
- options.input = path.normalize(options.input+'/');
39
- }
40
- if (options.output) {
41
- options.output = path.normalize(options.output+'/');
42
- }
43
-
44
- //wkhtmltopdf path does not need a trailing slash
45
- if (options.wkhtmltopdfPath && options.wkhtmltopdfPath !== '') {
46
- options.wkhtmltopdfPath = path.normalize(options.wkhtmltopdfPath);
47
- }
49
+ //wkhtmltopdf path does not need a trailing slash
50
+ if (options.wkhtmltopdfPath && options.wkhtmltopdfPath !== '') {
51
+ options.wkhtmltopdfPath = path.normalize(options.wkhtmltopdfPath);
48
52
  }
53
+ };
49
54
 
50
- /*
51
- copy the example source files (template) to any directory, when scaffold command is invoked
52
- */
55
+ /*
56
+ copy the example source files (template) to any directory, when scaffold command is invoked
57
+ */
53
58
 
54
- this.scaffold = function () {
55
- console.log(chalk.green('Creating scaffold template directory'));
56
- copyDirSync(__dirname+'/example', options.output);
57
- }
59
+ this.scaffold = () => {
60
+ console.log(chalk.green('Creating scaffold template directory'));
61
+ copyDirSync(__dirname + '/example', options.output);
62
+ };
58
63
 
59
- this.run = function () {
60
- console.log(chalk.green.bold('DocGen version '+version));
61
- //delete and recreate the output directory
62
- remakeDirSync(options.output);
63
- loadTemplates();
64
- }
64
+ this.run = () => {
65
+ console.log(chalk.green.bold('DocGen version ' + version));
66
+ //delete and recreate the output directory
67
+ remakeDirSync(options.output);
68
+ loadTemplates();
69
+ };
65
70
 
66
- /*
71
+ /*
67
72
  read any file (async)
68
73
  */
69
74
 
70
- var readFile = function (path) {
71
- return new rsvp.Promise(function (resolve, reject) {
72
- fs.readFile (path, 'utf8', function (error, data) {
73
- if (error) {
74
- console.log(chalk.red('Error reading file: '+path));
75
- reject(error);
76
- } else {
77
- data = data.replace(/^\uFEFF/, ''); //remove the BOM (byte-order-mark) from UTF-8 files, if present
78
- resolve(data);
79
- }
80
- });
81
- });
82
- }
75
+ let readFile = (path) => {
76
+ return new rsvp.Promise((resolve, reject) => {
77
+ fs.readFile(path, 'utf8', (error, data) => {
78
+ if (error) {
79
+ console.log(chalk.red('Error reading file: ' + path));
80
+ reject(error);
81
+ } else {
82
+ data = data.replace(/^\uFEFF/, ''); //remove the BOM (byte-order-mark) from UTF-8 files, if present
83
+ resolve(data);
84
+ }
85
+ });
86
+ });
87
+ };
83
88
 
84
- /*
89
+ /*
85
90
  write any file (async)
86
91
  */
87
92
 
88
- var writeFile = function (path, data) {
89
- return new rsvp.Promise(function (resolve, reject) {
90
- fs.writeFile(path, data, function (error) {
91
- if (error) {
92
- console.log(chalk.red('Error writing file: '+path));
93
- reject(error);
94
- } else {
95
- resolve(true);
96
- }
97
- });
98
- });
99
- }
93
+ let writeFile = (path, data) => {
94
+ return new rsvp.Promise((resolve, reject) => {
95
+ fs.writeFile(path, data, (error) => {
96
+ if (error) {
97
+ console.log(chalk.red('Error writing file: ' + path));
98
+ reject(error);
99
+ } else {
100
+ resolve(true);
101
+ }
102
+ });
103
+ });
104
+ };
100
105
 
101
- /*
106
+ /*
102
107
  copy any directory (sync)
103
108
  */
104
109
 
105
- var copyDirSync = function (source, destination) {
106
- try {
107
- fs.copySync(source, destination);
108
- } catch (error) {
109
- console.log(chalk.red('Error copying directory: '+source+' to '+destination));
110
- if (options.verbose === true) {
111
- console.log(chalk.red(error));
112
- mainProcess.exit(1);
113
- }
114
- }
110
+ let copyDirSync = (source, destination) => {
111
+ try {
112
+ fs.copySync(source, destination);
113
+ } catch (error) {
114
+ console.log(
115
+ chalk.red('Error copying directory: ' + source + ' to ' + destination),
116
+ );
117
+ if (options.verbose === true) {
118
+ console.log(chalk.red(error));
119
+ mainProcess.exit(1);
120
+ }
115
121
  }
122
+ };
116
123
 
117
- /*
124
+ /*
118
125
  remake a directory (sync) ... remove and then mkdir in one operation
119
126
  */
120
127
 
121
- var remakeDirSync = function (path) {
122
- try {
123
- fs.removeSync(path);
124
- fs.mkdirpSync(path);
125
- } catch (error) {
126
- console.log(chalk.red('Error recreating directory: '+path));
127
- if (options.verbose === true) {
128
- console.log(chalk.red(error));
129
- mainProcess.exit(1);
130
- }
131
- }
128
+ let remakeDirSync = (path) => {
129
+ try {
130
+ fs.removeSync(path);
131
+ fs.mkdirpSync(path);
132
+ } catch (error) {
133
+ console.log(chalk.red('Error recreating directory: ' + path));
134
+ if (options.verbose === true) {
135
+ console.log(chalk.red(error));
136
+ mainProcess.exit(1);
137
+ }
132
138
  }
139
+ };
133
140
 
134
- /*
141
+ /*
135
142
  remove any directory (sync)
136
143
  */
137
144
 
138
- var removeDirSync = function (path) {
139
- try {
140
- fs.removeSync(path);
141
- } catch (error) {
142
- console.log(chalk.red('Error removing directory: '+path));
143
- if (options.verbose === true) {
144
- console.log(chalk.red(error));
145
- mainProcess.exit(1);
146
- }
147
- }
145
+ let removeDirSync = (path) => {
146
+ try {
147
+ fs.removeSync(path);
148
+ } catch (error) {
149
+ console.log(chalk.red('Error removing directory: ' + path));
150
+ if (options.verbose === true) {
151
+ console.log(chalk.red(error));
152
+ mainProcess.exit(1);
153
+ }
148
154
  }
155
+ };
149
156
 
150
- /*
157
+ /*
151
158
  load all HTML template files
152
159
  */
153
160
 
154
- var loadTemplates = function () {
155
- console.log(chalk.green('Loading templates'));
156
- var files = {
157
- main: readFile(__dirname+'/templates/main.html'),
158
- redirect: readFile(__dirname+'/templates/redirect.html'),
159
- webCover: readFile(__dirname+'/templates/webCover.html'),
160
- pdfCover: readFile(__dirname+'/templates/pdfCover.html'),
161
- pdfHeader: readFile(__dirname+'/templates/pdfHeader.html'),
162
- pdfFooter: readFile(__dirname+'/templates/pdfFooter.html'),
163
- };
164
- rsvp.hash(files).then(function(files) {
165
- for (var key in files) {
166
- if (files.hasOwnProperty(key)) {
167
- var file = files[key];
168
- var dom = cheerio.load(file);
169
- templates[key] = dom;
170
- }
171
- }
172
- loadMeta();
173
- }).catch(function(error) {
174
- console.log(chalk.red('Error loading templates'));
175
- if (options.verbose === true) {
176
- console.log(chalk.red(error));
177
- }
178
- mainProcess.exit(1);
179
- });
180
- }
181
-
182
- /*
183
- JSON schema validation
184
- */
185
-
186
- var schemas = {
187
- "parameters" : {
188
- title: "DocGen Parameters Schema",
189
- type: "object",
190
- required: [
191
- "title",
192
- "name",
193
- "version",
194
- "date",
195
- "organization",
196
- "author",
197
- "owner",
198
- "contributors",
199
- "website",
200
- "module",
201
- "id",
202
- "summary",
203
- "marking",
204
- "legalese"
205
- ],
206
- properties: {
207
- title: { type: "string" },
208
- name: { type: "string" },
209
- version: { type: "string" },
210
- date: { type: "string" },
211
- organization: {
212
- type : "object",
213
- required: [ "name", "url"],
214
- properties: {
215
- name: { type: "string" },
216
- url: { type: "string" },
217
- }
218
- },
219
- author: {
220
- type : "object",
221
- required: [ "name", "url"],
222
- properties: {
223
- name: { type: "string" },
224
- url: { type: "string" },
225
- }
226
- },
227
- owner: {
228
- type : "object",
229
- required: [ "name", "url"],
230
- properties: {
231
- name: { type: "string" },
232
- url: { type: "string" },
233
- }
234
- },
235
- contributors: {
236
- type : "array",
237
- items: { oneOf: [ {
238
- type: "object",
239
- required: [ "name", "url"],
240
- properties: {
241
- name: { type: "string" },
242
- url: { type: "string" },
243
- }
244
- }]}
245
- },
246
- website: {
247
- type : "object",
248
- required: [ "name", "url"],
249
- properties: {
250
- name: { type: "string" },
251
- url: { type: "string" },
252
- }
253
- },
254
- backlink: {
255
- type : "object",
256
- required: [ "name", "url"],
257
- properties: {
258
- name: { type: "string" },
259
- url: { type: "string" },
260
- }
261
- },
262
- module: { type: "string" },
263
- id: { type: "string" },
264
- summary: { type: "string" },
265
- marking: { type: "string" },
266
- legalese: { type: "string" },
267
- }
161
+ let loadTemplates = () => {
162
+ console.log(chalk.green('Loading templates'));
163
+ let files = {
164
+ main: readFile(__dirname + '/templates/main.html'),
165
+ redirect: readFile(__dirname + '/templates/redirect.html'),
166
+ webCover: readFile(__dirname + '/templates/webCover.html'),
167
+ pdfCover: readFile(__dirname + '/templates/pdfCover.html'),
168
+ pdfHeader: readFile(__dirname + '/templates/pdfHeader.html'),
169
+ pdfFooter: readFile(__dirname + '/templates/pdfFooter.html'),
170
+ };
171
+ rsvp
172
+ .hash(files)
173
+ .then((files) => {
174
+ for (let key in files) {
175
+ if (files.hasOwnProperty(key)) {
176
+ let file = files[key];
177
+ let dom = cheerio.load(file);
178
+ templates[key] = dom;
179
+ }
180
+ }
181
+ loadMeta();
182
+ })
183
+ .catch((error) => {
184
+ console.log(chalk.red('Error loading templates'));
185
+ if (options.verbose === true) {
186
+ console.log(chalk.red(error));
187
+ }
188
+ mainProcess.exit(1);
189
+ });
190
+ };
191
+
192
+ /*
193
+ JSON schema validation
194
+ */
195
+
196
+ let schemas = {
197
+ parameters: {
198
+ title: 'DocGen Parameters Schema',
199
+ type: 'object',
200
+ required: [
201
+ 'title',
202
+ 'name',
203
+ 'version',
204
+ 'date',
205
+ 'organization',
206
+ 'author',
207
+ 'owner',
208
+ 'contributors',
209
+ 'website',
210
+ 'module',
211
+ 'id',
212
+ 'summary',
213
+ 'marking',
214
+ 'legalese',
215
+ ],
216
+ properties: {
217
+ title: { type: 'string' },
218
+ name: { type: 'string' },
219
+ version: { type: 'string' },
220
+ date: { type: 'string' },
221
+ organization: {
222
+ type: 'object',
223
+ required: ['name', 'url'],
224
+ properties: {
225
+ name: { type: 'string' },
226
+ url: { type: 'string' },
227
+ },
228
+ },
229
+ author: {
230
+ type: 'object',
231
+ required: ['name', 'url'],
232
+ properties: {
233
+ name: { type: 'string' },
234
+ url: { type: 'string' },
235
+ },
236
+ },
237
+ owner: {
238
+ type: 'object',
239
+ required: ['name', 'url'],
240
+ properties: {
241
+ name: { type: 'string' },
242
+ url: { type: 'string' },
243
+ },
268
244
  },
269
- "contents" : {
270
- title: "DocGen Table of Contents Schema",
271
- type : "array",
272
- items: { oneOf: [ {
273
- type: "object",
274
- required: [ "heading", "column", "pages"],
245
+ contributors: {
246
+ type: 'array',
247
+ items: {
248
+ oneOf: [
249
+ {
250
+ type: 'object',
251
+ required: ['name', 'url'],
275
252
  properties: {
276
- name: { type: "string" },
277
- column: { type: "integer", minimum: 1, maximum: 4 },
278
- pages: {
279
- type : "array",
280
- items: { oneOf: [ {
281
- type: "object",
282
- required: [ "title", "source"],
283
- properties: {
284
- title: { type: "string" },
285
- source: { type: "string" },
286
- html: { type: "boolean" },
287
- }
288
- }]}
253
+ name: { type: 'string' },
254
+ url: { type: 'string' },
255
+ },
256
+ },
257
+ ],
258
+ },
259
+ },
260
+ website: {
261
+ type: 'object',
262
+ required: ['name', 'url'],
263
+ properties: {
264
+ name: { type: 'string' },
265
+ url: { type: 'string' },
266
+ },
267
+ },
268
+ backlink: {
269
+ type: 'object',
270
+ required: ['name', 'url'],
271
+ properties: {
272
+ name: { type: 'string' },
273
+ url: { type: 'string' },
274
+ },
275
+ },
276
+ module: { type: 'string' },
277
+ id: { type: 'string' },
278
+ summary: { type: 'string' },
279
+ marking: { type: 'string' },
280
+ legalese: { type: 'string' },
281
+ },
282
+ },
283
+ contents: {
284
+ title: 'DocGen Table of Contents Schema',
285
+ type: 'array',
286
+ items: {
287
+ oneOf: [
288
+ {
289
+ type: 'object',
290
+ required: ['heading', 'column', 'pages'],
291
+ properties: {
292
+ name: { type: 'string' },
293
+ column: { type: 'integer', minimum: 1, maximum: 4 },
294
+ pages: {
295
+ type: 'array',
296
+ items: {
297
+ oneOf: [
298
+ {
299
+ type: 'object',
300
+ required: ['title', 'source'],
301
+ properties: {
302
+ title: { type: 'string' },
303
+ source: { type: 'string' },
304
+ html: { type: 'boolean' },
305
+ },
289
306
  },
290
- }
291
- }]}
292
- }
307
+ ],
308
+ },
309
+ },
310
+ },
311
+ },
312
+ ],
313
+ },
314
+ },
315
+ };
316
+
317
+ let validateJSON = (key, data) => {
318
+ let schema = schemas[key];
319
+ let validator = new schemaValidator();
320
+ let valid = validator.validate(data, schema);
321
+ if (!valid) {
322
+ console.log(
323
+ chalk.red(
324
+ 'Error parsing required file: ' +
325
+ key +
326
+ '.json (failed schema validation)',
327
+ ),
328
+ );
329
+ if (options.verbose === true) {
330
+ console.log(chalk.red(validator.getLastError()));
331
+ }
332
+ }
333
+ return valid;
334
+ };
335
+
336
+ /*
337
+ load all metadata files (JSON)
338
+ */
339
+
340
+ let loadMeta = () => {
341
+ console.log(chalk.green('Loading required JSON metadata files'));
342
+ let files = {
343
+ parameters: readFile(options.input + '/parameters.json'),
344
+ contents: readFile(options.input + '/contents.json'),
293
345
  };
294
-
295
- var validateJSON = function (key, data) {
296
- var schema = schemas[key];
297
- var validator = new schemaValidator();
298
- var valid = validator.validate(data, schema);
299
- if (!valid) {
300
- console.log(chalk.red('Error parsing required file: '+key+'.json (failed schema validation)'));
301
- if (options.verbose === true) {
302
- console.log(chalk.red(validator.getLastError()));
346
+ rsvp
347
+ .hash(files)
348
+ .then((files) => {
349
+ for (let key in files) {
350
+ if (files.hasOwnProperty(key)) {
351
+ //ignore prototype
352
+ try {
353
+ let file = JSON.parse(files[key]);
354
+ if (validateJSON(key, file)) {
355
+ meta[key] = file;
356
+ } else {
357
+ mainProcess.exit(1);
358
+ }
359
+ } catch (error) {
360
+ console.log(
361
+ chalk.red(
362
+ 'Error parsing required file: ' +
363
+ key +
364
+ '.json (invalid JSON)',
365
+ ),
366
+ );
367
+ if (options.verbose === true) {
368
+ console.log(chalk.red(error));
369
+ }
370
+ mainProcess.exit(1);
303
371
  }
372
+ }
304
373
  }
305
- return valid;
306
- }
307
-
308
- /*
309
- load all metadata files (JSON)
310
- */
311
-
312
- var loadMeta = function () {
313
- console.log(chalk.green('Loading required JSON metadata files'));
314
- var files = {
315
- parameters: readFile(options.input+'/parameters.json'),
316
- contents: readFile(options.input+'/contents.json'),
374
+ //add the release notes to the contents list
375
+ let extra = {
376
+ heading: 'Extra',
377
+ column: 5,
378
+ pages: [{ title: 'Release notes', source: 'release-notes.txt' }],
317
379
  };
318
- rsvp.hash(files).then(function(files) {
319
- for(var key in files) {
320
- if (files.hasOwnProperty(key)) { //ignore prototype
321
- try {
322
- var file = JSON.parse(files[key]);
323
- if (validateJSON(key, file)) {
324
- meta[key] = file;
325
- } else {
326
- mainProcess.exit(1);
327
- }
328
- } catch (error) {
329
- console.log(chalk.red('Error parsing required file: '+key+'.json (invalid JSON)'));
330
- if (options.verbose === true) {
331
- console.log(chalk.red(error));
332
- }
333
- mainProcess.exit(1);
334
- }
335
- }
380
+ meta.contents.push(extra);
381
+ loadMarkdown();
382
+ })
383
+ .catch((error) => {
384
+ console.log(chalk.red('Error loading required JSON metadata files'));
385
+ if (options.verbose === true) {
386
+ console.log(chalk.red(error));
387
+ }
388
+ mainProcess.exit(1);
389
+ });
390
+ };
391
+
392
+ /*
393
+ load all markdown files (source)
394
+ */
395
+
396
+ let loadMarkdown = () => {
397
+ console.log(chalk.green('Loading source files'));
398
+ let keys = [];
399
+ let files = [];
400
+ meta.contents.forEach((section) => {
401
+ section.pages.forEach((page) => {
402
+ keys.push(page);
403
+ files.push(options.input + '/' + page.source);
404
+ });
405
+ });
406
+ //add the release notes page
407
+ keys.push('ownership');
408
+ files.push(options.input + '/release-notes.txt');
409
+ rsvp
410
+ .all(files.map(readFile))
411
+ .then((files) => {
412
+ files.forEach((page, index) => {
413
+ try {
414
+ let key = keys[index];
415
+ if (key.html === true) {
416
+ //allow raw HTML input pages
417
+ pages[key.source] = page;
418
+ } else {
419
+ //otherwise parse input from Markdown into HTML
420
+ let html = markdown.render(page);
421
+ pages[key.source] = html;
336
422
  }
337
- //add the release notes to the contents list
338
- var extra = {
339
- heading: 'Extra',
340
- column: 5,
341
- pages: [
342
- { title: 'Release notes', source: 'release-notes.txt' }
343
- ]
344
- };
345
- meta.contents.push(extra);
346
- loadMarkdown();
347
- }).catch(function(error) {
348
- console.log(chalk.red('Error loading required JSON metadata files'));
423
+ } catch (error) {
424
+ console.log(
425
+ chalk.red('Error parsing Markdown file: ' + file.source),
426
+ );
349
427
  if (options.verbose === true) {
350
- console.log(chalk.red(error));
428
+ console.log(chalk.red(error));
351
429
  }
352
430
  mainProcess.exit(1);
431
+ }
353
432
  });
354
- }
355
-
356
- /*
357
- load all markdown files (source)
358
- */
359
-
360
- var loadMarkdown = function () {
361
- console.log(chalk.green('Loading source files'));
362
- var keys = [];
363
- var files = [];
364
- meta.contents.forEach( function (section) {
365
- section.pages.forEach( function (page) {
366
- keys.push(page);
367
- files.push(options.input+'/'+page.source);
368
- });
369
- });
370
- //add the release notes page
371
- keys.push('ownership');
372
- files.push(options.input+'/release-notes.txt');
373
- rsvp.all(files.map(readFile)).then(function (files) {
374
- files.forEach( function (page, index) {
375
- try{
376
- var key = keys[index];
377
- if (key.html === true) { //allow raw HTML input pages
378
- pages[key.source] = page;
379
- } else { //otherwise parse input from Markdown into HTML
380
- var html = markdown.render(page);
381
- pages[key.source] = html;
382
- }
383
- } catch (error) {
384
- console.log(chalk.red('Error parsing Markdown file: '+file.source));
385
- if (options.verbose === true) {
386
- console.log(chalk.red(error));
387
- }
388
- mainProcess.exit(1);
389
- }
433
+ processContent();
434
+ })
435
+ .catch((error) => {
436
+ console.log(error);
437
+ console.log(chalk.red('Error loading source files'));
438
+ if (options.verbose === true) {
439
+ console.log(chalk.red(error));
440
+ }
441
+ mainProcess.exit(1);
442
+ });
443
+ };
444
+
445
+ let sortPages = () => {
446
+ //sort the contents by heading
447
+ let headings = { 1: [], 2: [], 3: [], 4: [], 5: [] };
448
+ meta.contents.forEach((section) => {
449
+ if (headings.hasOwnProperty(section.column)) {
450
+ headings[section.column].push(section);
451
+ }
452
+ });
453
+ sortedPages = headings;
454
+ };
455
+
456
+ /*
457
+ build the HTML for the table of contents
458
+ */
459
+
460
+ let webToc = () => {
461
+ sortPages();
462
+ let pdfName = meta.parameters.name.toLowerCase() + '.pdf';
463
+ let $ = templates.main;
464
+ let html = [],
465
+ i = -1;
466
+ html[++i] = '<div><table class="unstyled"><tr>';
467
+ //build the contents HTML
468
+ for (let key in sortedPages) {
469
+ if (sortedPages.hasOwnProperty(key)) {
470
+ if (key != 5) {
471
+ //skip the extra column
472
+ html[++i] = '<td class="dg-tocGroup">';
473
+ sortedPages[key].forEach((section) => {
474
+ html[++i] =
475
+ '<ul><li class="dg-tocHeading">' + section.heading + '</li>';
476
+ section.pages.forEach((page) => {
477
+ let name = page.source.substr(0, page.source.lastIndexOf('.'));
478
+ let path = name + '.html';
479
+ html[++i] =
480
+ '<li><a href="' + path + '">' + page.title + '</a></li>';
390
481
  });
391
- process();
392
- }).catch(function(error) {
393
- console.log(chalk.red('Error loading source files'));
394
- if (options.verbose === true) {
395
- console.log(chalk.red(error));
396
- }
397
- mainProcess.exit(1);
398
- });
482
+ html[++i] = '</li></ul>';
483
+ });
484
+ html[++i] = '</td>';
485
+ }
486
+ }
399
487
  }
400
488
 
401
- var sortPages = function () {
402
- //sort the contents by heading
403
- var headings = {1: [], 2: [], 3: [], 4: [], 5: []};
404
- meta.contents.forEach( function (section) {
405
- if (headings.hasOwnProperty(section.column)) {
406
- headings[section.column].push(section);
407
- }
408
-
409
- });
410
- sortedPages = headings;
489
+ //fixed-width column at end
490
+ html[++i] = '<td class="dg-tocGroup" id="dg-tocFixedColumn"><ul>';
491
+ html[++i] =
492
+ '<li><span class="w-icon dg-tocIcon" data-name="person_group" title="archive"></span><a href="ownership.html">Ownership</a></li>';
493
+ html[++i] =
494
+ '<li><span class="w-icon dg-tocIcon" data-name="refresh" title="archive"></span><a href="release-notes.html">Release Notes</a></li>';
495
+ html[++i] = '</ul><div>';
496
+ if (options.pdf) {
497
+ html[++i] =
498
+ '<button class="w-icon-button" onclick="window.location=\'' +
499
+ pdfName +
500
+ '\';">';
501
+ html[++i] = '<span class="w-icon" data-name="document"></span>';
502
+ html[++i] = '<span>PDF copy</span>';
503
+ html[++i] = '</button>';
411
504
  }
412
-
413
- /*
414
- build the HTML for the table of contents
415
- */
416
-
417
- var webToc = function () {
418
- sortPages();
419
- var pdfName = meta.parameters.name.toLowerCase()+'.pdf';
420
- var $ = templates.main;
421
- var html = [], i = -1;
422
- html[++i] = '<div><table class="unstyled"><tr>';
423
- //build the contents HTML
424
- for (var key in sortedPages) {
425
- if (sortedPages.hasOwnProperty(key)) {
426
- if (key != 5) { //skip the extra column
427
- html[++i] = '<td class="dg-tocGroup">';
428
- sortedPages[key].forEach( function (section) {
429
- html[++i] = '<ul><li class="dg-tocHeading">'+section.heading+'</li>';
430
- section.pages.forEach( function (page) {
431
- var name = page.source.substr(0, page.source.lastIndexOf('.'));
432
- var path = name+'.html';
433
- html[++i] = '<li><a href="'+path+'">'+page.title+'</a></li>';
434
- });
435
- html[++i] = '</li></ul>';
436
- });
437
- html[++i] = '</td>';
438
- }
439
- }
440
- }
441
-
442
- //fixed-width column at end
443
- html[++i] = '<td class="dg-tocGroup" id="dg-tocFixedColumn"><ul>';
444
- html[++i] = '<li><span class="w-icon dg-tocIcon" data-name="person_group" title="archive"></span><a href="ownership.html">Ownership</a></li>';
445
- html[++i] = '<li><span class="w-icon dg-tocIcon" data-name="refresh" title="archive"></span><a href="release-notes.html">Release Notes</a></li>';
446
- html[++i] = '</ul><div>';
447
- if (options.pdf) {
448
- html[++i] = '<button class="w-icon-button" onclick="window.location=\''+pdfName+'\';">';
449
- html[++i] = '<span class="w-icon" data-name="document"></span>';
450
- html[++i] = '<span>PDF copy</span>';
451
- html[++i] = '</button>';
452
- }
453
- html[++i] = '</div></td>';
454
- html[++i] = '</tr></table></div>';
455
- $('#dg-toc').html(html.join(''));
456
- templates.main = $;
505
+ html[++i] = '</div></td>';
506
+ html[++i] = '</tr></table></div>';
507
+ $('#dg-toc').html(html.join(''));
508
+ templates.main = $;
509
+ };
510
+
511
+ /*
512
+ insert the parameters into all templates
513
+ */
514
+
515
+ let insertParameters = () => {
516
+ //------------------------------------------------------------------------------------------------------
517
+ //logo dimensions
518
+ let hasLogo = false;
519
+ let logoWidth = 0;
520
+ let logoHeight = 0;
521
+ try {
522
+ let logo = imageSizeOf(options.input + '/files/images/logo.png');
523
+ logoWidth = logo.width;
524
+ logoHeight = logo.height;
525
+ hasLogo = true;
526
+ } catch (error) {
527
+ //do nothing. If logo file cannot be read, logo is simply not shown
457
528
  }
458
529
 
459
- /*
460
- insert the parameters into all templates
461
- */
530
+ //------------------------------------------------------------------------------------------------------
462
531
 
463
- var insertParameters = function () {
532
+ //the homepage is the first link in the first heading
533
+ let homelink = meta.contents[0].pages[0];
534
+ homelink =
535
+ homelink.source.substr(0, homelink.source.lastIndexOf('.')) + '.html';
464
536
 
465
- //------------------------------------------------------------------------------------------------------
466
- //logo dimensions
467
- var hasLogo = false;
468
- var logoWidth = 0;
469
- var logoHeight = 0;
470
- try {
471
- var logo = imageSizeOf(options.input+'/files/images/logo.png');
472
- logoWidth = logo.width;
473
- logoHeight = logo.height;
474
- hasLogo = true;
475
- } catch (error) {
476
- //do nothing. If logo file cannot be read, logo is simply not shown
477
- }
537
+ let date = moment().format('DD/MM/YYYY');
538
+ let time = moment().format('HH:mm:ss');
539
+ let year = moment().format('YYYY');
540
+ let attribution =
541
+ 'Created by DocGen ' + version + ' on ' + date + ' at ' + time + '.';
478
542
 
479
- //------------------------------------------------------------------------------------------------------
543
+ let releaseVersion = meta.parameters.version;
544
+ if (options.setVersion !== false) {
545
+ releaseVersion = options.setVersion;
546
+ }
547
+ let releaseDate = meta.parameters.date;
548
+ if (options.setReleaseDate !== false) {
549
+ releaseDate = options.setReleaseDate;
550
+ }
480
551
 
481
- //the homepage is the first link in the first heading
482
- var homelink = meta.contents[0].pages[0];
483
- var homelink = homelink.source.substr(0, homelink.source.lastIndexOf('.'))+'.html';
552
+ let author = '';
553
+ if (meta.parameters.author.url !== '') {
554
+ author +=
555
+ '<a href="' +
556
+ meta.parameters.author.url +
557
+ '">' +
558
+ meta.parameters.author.name +
559
+ '</a>';
560
+ } else {
561
+ author += meta.parameters.author.name;
562
+ }
484
563
 
485
- var date = moment().format('DD/MM/YYYY');
486
- var time = moment().format('HH:mm:ss');
487
- var year = moment().format('YYYY');
488
- var attribution = 'Created by DocGen '+version+' on '+date+' at '+time+'.';
564
+ let owner = '';
565
+ if (meta.parameters.owner.url !== '') {
566
+ owner +=
567
+ '<a href="' +
568
+ meta.parameters.owner.url +
569
+ '">' +
570
+ meta.parameters.owner.name +
571
+ '</a>';
572
+ } else {
573
+ owner += meta.parameters.owner.name;
574
+ }
489
575
 
490
- var releaseVersion = meta.parameters.version;
491
- if (options.setVersion !== false) {
492
- releaseVersion = options.setVersion;
493
- }
494
- var releaseDate = meta.parameters.date;
495
- if (options.setReleaseDate !== false) {
496
- releaseDate = options.setReleaseDate;
497
- }
576
+ let organization = '';
577
+ if (meta.parameters.organization.url !== '') {
578
+ organization +=
579
+ '<a href="' +
580
+ meta.parameters.organization.url +
581
+ '">' +
582
+ meta.parameters.organization.name +
583
+ '</a>';
584
+ } else {
585
+ organization += meta.parameters.organization.name;
586
+ }
498
587
 
499
- var author = '';
500
- if (meta.parameters.author.url !== '') {
501
- author += '<a href="'+meta.parameters.author.url+'">'+meta.parameters.author.name+'</a>';
502
- } else {
503
- author += meta.parameters.author.name;
504
- }
588
+ let website = '';
589
+ if (meta.parameters.website.url !== '') {
590
+ website +=
591
+ '<a href="' +
592
+ meta.parameters.website.url +
593
+ '">' +
594
+ meta.parameters.website.name +
595
+ '</a>';
596
+ } else {
597
+ website += meta.parameters.website.name;
598
+ }
505
599
 
506
- var owner = '';
507
- if (meta.parameters.owner.url !== '') {
508
- owner += '<a href="'+meta.parameters.owner.url+'">'+meta.parameters.owner.name+'</a>';
509
- } else {
510
- owner += meta.parameters.owner.name;
511
- }
600
+ let backlink = '';
601
+ if (meta.parameters.backlink.url !== '') {
602
+ backlink +=
603
+ '<a href="' +
604
+ meta.parameters.backlink.url +
605
+ '">' +
606
+ meta.parameters.backlink.name +
607
+ '</a>';
608
+ } else {
609
+ backlink += meta.parameters.backlink.name;
610
+ }
512
611
 
513
- var organization = '';
514
- if (meta.parameters.organization.url !== '') {
515
- organization += '<a href="'+meta.parameters.organization.url+'">'+meta.parameters.organization.name+'</a>';
612
+ let contributors = '';
613
+ meta.parameters.contributors.forEach((contributor) => {
614
+ if (contributor.url !== '') {
615
+ contributors +=
616
+ '<a href="' + contributor.url + '">' + contributor.name + '</a>, ';
617
+ } else {
618
+ contributors += contributor.name + ', ';
619
+ }
620
+ });
621
+ contributors = contributors.replace(/,\s*$/, ''); //remove trailing commas
622
+
623
+ let copyright = '&copy; ' + year + ' ' + organization;
624
+
625
+ let webTitle = meta.parameters.title;
626
+
627
+ let webFooter =
628
+ 'Version ' + releaseVersion + ' released on ' + releaseDate + '.';
629
+
630
+ for (let key in templates) {
631
+ if (templates.hasOwnProperty(key)) {
632
+ let $ = templates[key];
633
+ //logo
634
+ if (hasLogo === true) {
635
+ let logoUrl = 'files/images/logo.png';
636
+ $('#dg-logo').css('background-image', 'url(' + logoUrl + ')');
637
+ $('#dg-logo').css('height', logoHeight + 'px');
638
+ $('#dg-logo').css('line-height', logoHeight + 'px');
639
+ $('#dg-logo').css('padding-left', logoWidth + 25 + 'px');
516
640
  } else {
517
- organization += meta.parameters.organization.name;
641
+ $('#dg-logo').css('padding-left', '0');
518
642
  }
519
-
520
- var website = '';
521
- if (meta.parameters.website.url !== '') {
522
- website += '<a href="'+meta.parameters.website.url+'">'+meta.parameters.website.name+'</a>';
643
+ //parameters
644
+ $('title').text(meta.parameters.title);
645
+ $('#dg-homelink').attr('href', homelink);
646
+ $('#dg-title').text(meta.parameters.title);
647
+ $('#dg-owner').html(owner);
648
+ $('#dg-version').text(releaseVersion);
649
+ $('#dg-web-title-version').text('(' + releaseVersion + ')');
650
+ $('#dg-release-date').text(releaseDate);
651
+ $('#dg-web-footer').text(webFooter);
652
+ $('#dg-author').html(author);
653
+ $('#dg-contributors').html(contributors);
654
+ $('#dg-module').text(meta.parameters.module);
655
+ $('#dg-id').html(meta.parameters.id);
656
+ $('#dg-website').html(website);
657
+ $('#dg-backlink').html(backlink);
658
+ $('#dg-summary').text(meta.parameters.summary);
659
+ $('#dg-copyright').html(copyright);
660
+ $('#dg-marking').text(meta.parameters.marking);
661
+ $('#dg-legalese').text(meta.parameters.legalese);
662
+ $('#dg-attribution').text(attribution);
663
+ }
664
+ }
665
+ if (options.mathKatex === true) {
666
+ let $ = templates.main;
667
+ //support for KaTeX (bundled with DocGen)
668
+ $('head').append(
669
+ '<link rel="stylesheet" href="require/katex/katex.min.css" type="text/css">',
670
+ );
671
+ $('head').append(
672
+ '<script type="text/javascript" src="require/katex/katex.min.js"></script>',
673
+ );
674
+ $('head').append(
675
+ '<script type="text/javascript" src="require/katexInjector.js"></script>',
676
+ );
677
+ }
678
+ if (options.mathMathjax === true) {
679
+ //support for MathJax (only supported via CDN due to very large size)
680
+ //MathJax configuration is the same as used by math.stackexchange.com
681
+ //Note - wkhtmlpdf //cdn urls - see https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1634
682
+ $('head').append(
683
+ '<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML-full"></script>',
684
+ );
685
+ }
686
+ };
687
+
688
+ /*
689
+ process each input into an output
690
+ */
691
+
692
+ let processContent = () => {
693
+ console.log(chalk.green('Generating the static web content'));
694
+ webToc();
695
+ insertParameters();
696
+ meta.contents.forEach((section) => {
697
+ section.pages.forEach((page) => {
698
+ let $ = cheerio.load(templates.main.html()); //clone
699
+ let key = page.source;
700
+ let content = pages[key];
701
+ //add relevant container
702
+ if (page.html === true) {
703
+ //raw HTML pages should not be confined to the fixed width
704
+ $('#dg-content').html('<div id="dg-innerContent"></div>');
523
705
  } else {
524
- website += meta.parameters.website.name;
706
+ //Markdown pages should be confined to the fixed width
707
+ $('#dg-content').html(
708
+ '<div class="w-fixed-width"><div id="dg-innerContent"></div></div>',
709
+ );
525
710
  }
526
-
527
- var backlink = '';
528
- if (meta.parameters.backlink.url !== '') {
529
- backlink += '<a href="'+meta.parameters.backlink.url+'">'+meta.parameters.backlink.name+'</a>';
530
- } else {
531
- backlink += meta.parameters.backlink.name;
711
+ $('#dg-innerContent').html(content);
712
+ //------------------------------------------------------------------------------------------------------
713
+ //insert permalinks for every page heading
714
+ //when pageToc is enabled, also insert a page-level table of contents
715
+ let html = [],
716
+ i = -1;
717
+ let headings = $('h1, h2, h3, h4, h5, h6');
718
+ if (headings.length > 0) {
719
+ html[++i] = '<ul class="dg-pageToc">';
532
720
  }
533
-
534
- var contributors = '';
535
- meta.parameters.contributors.forEach (function (contributor) {
536
- if (contributor.url !== '') {
537
- contributors += '<a href="'+contributor.url+'">'+contributor.name+'</a>, ';
538
- } else {
539
- contributors += contributor.name+', ';
540
- }
721
+ headings.each(function () {
722
+ let label = $(this).text();
723
+ let anchor = label.toLowerCase().replace(/\s+/g, '-');
724
+ $(this).attr('id', anchor);
725
+ html[++i] = '<li><a href="#' + anchor + '">' + label + '</a></li>';
541
726
  });
542
- contributors = contributors.replace(/,\s*$/, ""); //remove trailing commas
543
-
544
- var copyright = '&copy; '+year+' '+organization;
545
-
546
- var webTitle = meta.parameters.title
547
-
548
- var webFooter = 'Version '+releaseVersion+' released on '+releaseDate+'.';
549
-
550
- for (var key in templates) {
551
- if (templates.hasOwnProperty(key)) {
552
- $ = templates[key];
553
- //logo
554
- if (hasLogo === true) {
555
- var logoUrl = 'files/images/logo.png';
556
- $('#dg-logo').css('background-image', 'url(' + logoUrl + ')');
557
- $('#dg-logo').css('height', logoHeight+'px');
558
- $('#dg-logo').css('line-height', logoHeight+'px');
559
- $('#dg-logo').css('padding-left', (logoWidth+25)+'px');
560
- } else {
561
- $('#dg-logo').css('padding-left', '0');
562
- }
563
- //parameters
564
- $('title').text(meta.parameters.title);
565
- $('#dg-homelink').attr('href', homelink);
566
- $('#dg-title').text(meta.parameters.title);
567
- $('#dg-owner').html(owner);
568
- $('#dg-version').text(releaseVersion);
569
- $('#dg-web-title-version').text('('+releaseVersion+')');
570
- $('#dg-release-date').text(releaseDate);
571
- $('#dg-web-footer').text(webFooter);
572
- $('#dg-author').html(author);
573
- $('#dg-contributors').html(contributors);
574
- $('#dg-module').text(meta.parameters.module);
575
- $('#dg-id').html(meta.parameters.id);
576
- $('#dg-website').html(website);
577
- $('#dg-backlink').html(backlink);
578
- $('#dg-summary').text(meta.parameters.summary);
579
- $('#dg-copyright').html(copyright);
580
- $('#dg-marking').text(meta.parameters.marking);
581
- $('#dg-legalese').text(meta.parameters.legalese);
582
- $('#dg-attribution').text(attribution);
583
- }
727
+ if (headings.length > 0) {
728
+ html[++i] = '</ul>';
584
729
  }
585
- if (options.mathKatex === true) {
586
- $ = templates.main;
587
- //support for KaTeX (bundled with DocGen)
588
- $('head').append('<link rel="stylesheet" href="require/katex/katex.min.css" type="text/css">');
589
- $('head').append('<script type="text/javascript" src="require/katex/katex.min.js"></script>');
590
- $('head').append('<script type="text/javascript" src="require/katexInjector.js"></script>');
591
-
730
+ if (options.pageToc === true && page.html !== true) {
731
+ $('#dg-innerContent').prepend(html.join(''));
592
732
  }
593
- if (options.mathMathjax === true) {
594
- //support for MathJax (only supported via CDN due to very large size)
595
- //MathJax configuration is the same as used by math.stackexchange.com
596
- //Note - wkhtmlpdf //cdn urls - see https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1634
597
- $('head').append('<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML-full"></script>');
733
+ //------------------------------------------------------------------------------------------------------
734
+ //prepend the auto heading (which makes the PDF table of contents match the web TOC)
735
+ $('#dg-innerContent').prepend(
736
+ '<h1 id="dg-autoTitle">' + page.title + '</h1>',
737
+ );
738
+ if (page.html === true) {
739
+ $('#dg-autoTitle').addClass('dg-hiddenTitle');
598
740
  }
741
+ //------------------------------------------------------------------------------------------------------
742
+ //apply the w-table class
743
+ $('table:not(.unstyled)').addClass('w-table w-fixed w-stripe');
744
+ //------------------------------------------------------------------------------------------------------
745
+ pages[key] = $;
746
+ });
747
+ });
748
+ //add web ownership page
749
+ let $ = cheerio.load(templates.main.html()); //clone
750
+ $('#dg-content').html(
751
+ '<div class="w-fixed-width"><div id="dg-innerContent"></div></div>',
752
+ );
753
+ $('#dg-innerContent').html(templates.webCover.html());
754
+ templates.webCover = $;
755
+ writePages();
756
+ };
757
+
758
+ /*
759
+ write each html page
760
+ */
761
+
762
+ let writePages = () => {
763
+ console.log(chalk.green('Writing the web page files'));
764
+ let promises = {};
765
+ meta.contents.forEach((section) => {
766
+ section.pages.forEach((page) => {
767
+ let key = page.source;
768
+ let name = key.substr(0, page.source.lastIndexOf('.'));
769
+ let path = options.output + name + '.html';
770
+ let html = pages[key].html();
771
+ promises[key] = writeFile(path, html);
772
+ });
773
+ });
774
+ //add extra files
775
+ promises['ownership'] = writeFile(
776
+ options.output + 'ownership.html',
777
+ templates.webCover.html(),
778
+ );
779
+ if (options.pdf === true) {
780
+ let pdfTempDir = options.output + 'temp/';
781
+ fs.mkdirsSync(pdfTempDir);
782
+ promises['docgenPdfCover'] = writeFile(
783
+ pdfTempDir + 'pdfCover.html',
784
+ templates.pdfCover.html(),
785
+ );
786
+ promises['docgenPdfHeader'] = writeFile(
787
+ pdfTempDir + 'pdfHeader.html',
788
+ templates.pdfHeader.html(),
789
+ );
790
+ promises['docgenPdfFooter'] = writeFile(
791
+ pdfTempDir + 'pdfFooter.html',
792
+ templates.pdfFooter.html(),
793
+ );
599
794
  }
600
-
601
- /*
602
- process each input into an output
603
- */
604
-
605
- var process = function () {
606
- console.log(chalk.green('Generating the static web content'));
607
- webToc();
608
- insertParameters();
609
- meta.contents.forEach( function (section) {
610
- section.pages.forEach( function (page) {
611
- var $ = cheerio.load(templates.main.html()); //clone
612
- var key = page.source;
613
- var content = pages[key];
614
- //add relevant container
615
- if (page.html === true) { //raw HTML pages should not be confined to the fixed width
616
- $('#dg-content').html('<div id="dg-innerContent"></div>');
617
- } else { //Markdown pages should be confined to the fixed width
618
- $('#dg-content').html('<div class="w-fixed-width"><div id="dg-innerContent"></div></div>');
619
- }
620
- $('#dg-innerContent').html(content);
621
- //------------------------------------------------------------------------------------------------------
622
- //insert permalinks for every page heading
623
- //when pageToc is enabled, also insert a page-level table of contents
624
- var html = [], i = -1;
625
- var headings = $('h1, h2, h3, h4, h5, h6');
626
- if (headings.length > 0) {
627
- html[++i] = '<ul class="dg-pageToc">';
628
- }
629
- headings.each(function( index ) {
630
- var label = $(this).text();
631
- var anchor = label.toLowerCase().replace(/\s+/g, "-");
632
- $(this).attr('id', anchor);
633
- html[++i] = '<li><a href="#'+anchor+'">'+label+'</a></li>';
634
- });
635
- if (headings.length > 0) {
636
- html[++i] = '</ul>';
637
- }
638
- if (options.pageToc === true && page.html !== true) {
639
- $('#dg-innerContent').prepend(html.join(''));
640
- }
641
- //------------------------------------------------------------------------------------------------------
642
- //prepend the auto heading (which makes the PDF table of contents match the web TOC)
643
- $('#dg-innerContent').prepend('<h1 id="dg-autoTitle">'+page.title+'</h1>');
644
- if (page.html === true) {
645
- $('#dg-autoTitle').addClass('dg-hiddenTitle');
646
- }
647
- //------------------------------------------------------------------------------------------------------
648
- //apply the w-table class
649
- $('table:not(.unstyled)').addClass('w-table w-fixed w-stripe');
650
- //------------------------------------------------------------------------------------------------------
651
- pages[key] = $;
652
- });
653
- });
654
- //add web ownership page
655
- var $ = cheerio.load(templates.main.html()); //clone
656
- $('#dg-content').html('<div class="w-fixed-width"><div id="dg-innerContent"></div></div>');
657
- $('#dg-innerContent').html(templates.webCover.html());
658
- templates.webCover = $;
659
- writePages();
660
- }
661
-
662
- /*
663
- write each html page
664
- */
665
-
666
- var writePages = function () {
667
- console.log(chalk.green('Writing the web page files'));
668
- var promises = {};
669
- meta.contents.forEach( function (section) {
670
- section.pages.forEach( function (page) {
671
- var key = page.source;
672
- var name = key.substr(0, page.source.lastIndexOf('.'));
673
- var path = options.output+name+'.html';
674
- var html = pages[key].html();
675
- promises[key] = writeFile(path, html);
676
- });
677
- });
678
- //add extra files
679
- promises['ownership'] = writeFile(options.output+'ownership.html', templates.webCover.html());
680
- if (options.pdf === true) {
681
- var pdfTempDir = options.output+'temp/';
682
- fs.mkdirsSync(pdfTempDir);
683
- promises['docgenPdfCover'] = writeFile(pdfTempDir+'pdfCover.html', templates.pdfCover.html());
684
- promises['docgenPdfHeader'] = writeFile(pdfTempDir+'pdfHeader.html', templates.pdfHeader.html());
685
- promises['docgenPdfFooter'] = writeFile(pdfTempDir+'pdfFooter.html', templates.pdfFooter.html());
795
+ rsvp
796
+ .hash(promises)
797
+ .then(() => {
798
+ copyDirSync(__dirname + '/require', options.output + 'require'); //CSS, JavaScript
799
+ copyDirSync(options.input + '/files', options.output + 'files'); //user-attached files and images
800
+ if (options.mathKatex === true) {
801
+ copyDirSync(
802
+ __dirname + '/optional/katex',
803
+ options.output + 'require/katex',
804
+ );
686
805
  }
687
- rsvp.hash(promises).then(function (files) {
688
- copyDirSync(__dirname+'/require', options.output+'require'); //CSS, JavaScript
689
- copyDirSync(options.input+'/files', options.output+'files'); //user-attached files and images
690
- if (options.mathKatex === true) {
691
- copyDirSync(__dirname+'/optional/katex', options.output+'require/katex');
692
- }
693
- checkPdfVersion();
694
- }).catch(function(error) {
695
- console.log(chalk.red('Error writing the web page files'));
696
- if (options.verbose === true) {
697
- console.log(chalk.red(error));
698
- }
699
- mainProcess.exit(1);
700
- });
701
- }
702
-
703
- /*
704
- wkthmltopdf options
705
- */
706
-
707
- var pdfOptions = [
708
- ' --zoom 1.0',
709
- ' --image-quality 100',
710
- ' --print-media-type',
711
- ' --orientation portrait',
712
- ' --page-size A4',
713
- ' --margin-top 25',
714
- ' --margin-right 15',
715
- ' --margin-bottom 16',
716
- ' --margin-left 15',
717
- ' --header-spacing 5',
718
- ' --footer-spacing 5',
719
- ' --no-stop-slow-scripts',
720
- ];
721
-
722
- var getPdfArguments = function () {
723
- var pdfName = meta.parameters.name.toLowerCase()+'.pdf';
724
- pdfOptions.push(' --javascript-delay '+options.pdfDelay); //code syntax highlight in wkhtmltopdf 0.12.2.1 fails without a delay (but why doesn't --no-stop-slow-scripts work?)
725
- pdfOptions.push(' --user-style-sheet '+__dirname+'/pdf-stylesheet.css');
726
- pdfOptions.push(' --header-html '+options.output+'temp/pdfHeader.html');
727
- pdfOptions.push(' --footer-html '+options.output+'temp/pdfFooter.html');
728
- pdfOptions.push(' cover '+options.output+'temp/pdfCover.html');
729
- pdfOptions.push(' toc --xsl-style-sheet '+__dirname+'/pdf-contents.xsl');
730
- var allPages = '';
731
- for (var key in sortedPages) {
732
- if (sortedPages.hasOwnProperty(key)) {
733
- sortedPages[key].forEach( function (section) {
734
- section.pages.forEach( function (page) {
735
- var key = page.source;
736
- var name = key.substr(0, page.source.lastIndexOf('.'));
737
- var path = options.output+name+'.html';
738
- allPages += ' '+path;
739
- });
740
- });
741
- }
806
+ checkPdfVersion();
807
+ })
808
+ .catch((error) => {
809
+ console.log(chalk.red('Error writing the web page files'));
810
+ if (options.verbose === true) {
811
+ console.log(chalk.red(error));
742
812
  }
743
- var args = pdfOptions.join('');
744
- args += allPages;
745
- args += ' '+options.output+pdfName;
746
- return spawnArgs(args);
813
+ mainProcess.exit(1);
814
+ });
815
+ };
816
+
817
+ /*
818
+ wkthmltopdf options
819
+ */
820
+
821
+ let pdfOptions = [
822
+ ' --zoom 1.0',
823
+ ' --image-quality 100',
824
+ ' --print-media-type',
825
+ ' --orientation portrait',
826
+ ' --page-size A4',
827
+ ' --margin-top 25',
828
+ ' --margin-right 15',
829
+ ' --margin-bottom 16',
830
+ ' --margin-left 15',
831
+ ' --header-spacing 5',
832
+ ' --footer-spacing 5',
833
+ ' --no-stop-slow-scripts',
834
+ ];
835
+
836
+ let getPdfArguments = () => {
837
+ let pdfName = meta.parameters.name.toLowerCase() + '.pdf';
838
+ pdfOptions.push(' --enable-local-file-access');
839
+ pdfOptions.push(' --javascript-delay ' + options.pdfDelay); //code syntax highlight in wkhtmltopdf 0.12.2.1 fails without a delay (but why doesn't --no-stop-slow-scripts work?)
840
+ pdfOptions.push(' --user-style-sheet ' + __dirname + '/pdf-stylesheet.css');
841
+ pdfOptions.push(' --header-html ' + options.output + 'temp/pdfHeader.html');
842
+ pdfOptions.push(' --footer-html ' + options.output + 'temp/pdfFooter.html');
843
+ pdfOptions.push(' cover ' + options.output + 'temp/pdfCover.html');
844
+ pdfOptions.push(
845
+ ' toc --xsl-style-sheet ' + __dirname + '/pdf-contents.xsl',
846
+ );
847
+ let allPages = '';
848
+ for (let key in sortedPages) {
849
+ if (sortedPages.hasOwnProperty(key)) {
850
+ sortedPages[key].forEach((section) => {
851
+ section.pages.forEach((page) => {
852
+ let key = page.source;
853
+ let name = key.substr(0, page.source.lastIndexOf('.'));
854
+ let path = options.output + name + '.html';
855
+ allPages += ' ' + path;
856
+ });
857
+ });
858
+ }
747
859
  }
748
-
749
- var checkPdfVersion = function () {
750
- if (options.pdf === true) {
751
- //first check that wkhtmltopdf is installed
752
- childProcess.exec(options.wkhtmltopdfPath+' -V', function (error, stdout, stderr) {
753
- if (error) {
754
- console.log(chalk.red('Unable to call wkhtmltopdf. Is it installed and in path? See http://wkhtmltopdf.org'));
755
- if (options.verbose === true) {
756
- console.log(chalk.red(error));
757
- }
758
- mainProcess.exit(1);
759
- } else {
760
- //warn if the version of wkhtmltopdf is not an expected version
761
- var actualWkhtmltopdfVersion = stdout.trim();
762
- if (actualWkhtmltopdfVersion !== wkhtmltopdfVersion) {
763
- var warning = 'Warning: unexpected version of wkhtmltopdf, which may work but is not tested or supported';
764
- var expectedVersion = ' expected version: '+wkhtmltopdfVersion;
765
- var detectedVersion = ' detected version: '+actualWkhtmltopdfVersion;
766
- console.log(chalk.yellow(warning));
767
- console.log(chalk.yellow(expectedVersion));
768
- console.log(chalk.yellow(detectedVersion));
769
- }
770
- generatePdf();
771
- }
772
- });
860
+ let args = pdfOptions.join('');
861
+ args += allPages;
862
+ args += ' ' + options.output + pdfName;
863
+ return spawnArgs(args);
864
+ };
865
+
866
+ let checkPdfVersion = () => {
867
+ if (options.pdf === true) {
868
+ //first check that wkhtmltopdf is installed
869
+ exec(options.wkhtmltopdfPath + ' -V', (error, stdout, stderr) => {
870
+ if (error) {
871
+ console.log(
872
+ chalk.red(
873
+ 'Unable to call wkhtmltopdf. Is it installed and in path? See http://wkhtmltopdf.org',
874
+ ),
875
+ );
876
+ if (options.verbose === true) {
877
+ console.log(chalk.red(error));
878
+ }
879
+ mainProcess.exit(1);
773
880
  } else {
774
- cleanUp();
881
+ //warn if the version of wkhtmltopdf is not an expected version
882
+ let actualWkhtmltopdfVersion = stdout.trim();
883
+ if (actualWkhtmltopdfVersion !== wkhtmltopdfVersion) {
884
+ let warning =
885
+ 'Warning: unexpected version of wkhtmltopdf, which may work but is not tested or supported';
886
+ let expectedVersion = ' expected version: ' + wkhtmltopdfVersion;
887
+ let detectedVersion =
888
+ ' detected version: ' + actualWkhtmltopdfVersion;
889
+ console.log(chalk.yellow(warning));
890
+ console.log(chalk.yellow(expectedVersion));
891
+ console.log(chalk.yellow(detectedVersion));
892
+ }
893
+ generatePdf();
775
894
  }
895
+ });
896
+ } else {
897
+ cleanUp();
776
898
  }
777
-
778
- /*
779
- call wkhtmltopdf as an external executable
780
- */
781
-
782
- var generatePdf = function () {
783
- console.log(chalk.green('Creating the PDF copy (may take some time)'));
784
- var args = getPdfArguments();
785
- var wkhtmltopdf = childProcess.spawn(options.wkhtmltopdfPath, args);
786
- var spinner = new cliSpinner(chalk.green(' Processing... %s'));
787
- spinner.setSpinnerString('|/-\\');
788
-
789
- wkhtmltopdf.on('error', function (error) {
790
- console.log(chalk.red('Error calling wkhtmltopdf to generate the PDF'));
791
- if (options.verbose === true) {
792
- console.log(chalk.red(error));
793
- }
794
- });
795
-
796
- if (options.verbose !== true) {
797
- spinner.start(); //only show spinner when verbose is off (otherwise show raw wkhtmltopdf output)
798
- } else {
799
- //pipe the output from wkhtmltopdf back to stdout
800
- //however, wkhtmltpdf outputs to stderr, not stdout. See:
801
- //https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1980
802
- wkhtmltopdf.stderr.pipe(mainProcess.stdout);
803
- }
804
-
805
- wkhtmltopdf.stdout.on('data', function (data) {
806
- //do nothing
807
- });
808
-
809
- wkhtmltopdf.stderr.on('data', function (data) {
810
- //do nothing
811
- });
812
-
813
- wkhtmltopdf.on('close', function (code) {
814
- if (options.verbose !== true) {
815
- spinner.stop();
816
- console.log(''); //newline after spinner stops
817
- }
818
- if (code !== 0) {
819
- var warning = 'wkhtmltopdf exited with a warning or error: try the -v option for details';
820
- console.log(chalk.yellow(warning));
821
- }
822
- cleanUp();
823
- });
899
+ };
900
+
901
+ /*
902
+ call wkhtmltopdf as an external executable
903
+ */
904
+
905
+ let generatePdf = () => {
906
+ console.log(chalk.green('Creating the PDF copy (may take some time)'));
907
+ let args = getPdfArguments();
908
+ let wkhtmltopdf = spawn(options.wkhtmltopdfPath, args);
909
+ let spinner = new cliSpinner(chalk.green(' Processing... %s'));
910
+ spinner.setSpinnerString('|/-\\');
911
+
912
+ wkhtmltopdf.on('error', (error) => {
913
+ console.log(chalk.red('Error calling wkhtmltopdf to generate the PDF'));
914
+ if (options.verbose === true) {
915
+ console.log(chalk.red(error));
916
+ }
917
+ });
918
+
919
+ if (options.verbose !== true) {
920
+ spinner.start(); //only show spinner when verbose is off (otherwise show raw wkhtmltopdf output)
921
+ } else {
922
+ //pipe the output from wkhtmltopdf back to stdout
923
+ //however, wkhtmltpdf outputs to stderr, not stdout. See:
924
+ //https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1980
925
+ wkhtmltopdf.stderr.pipe(mainProcess.stdout);
824
926
  }
825
927
 
826
- var createRedirect = function () {
827
- if (options.redirect) {
828
- var parent = options.output.replace(/\/$/, ""); //trim any trailing slash
829
- parent = parent.split(path.sep).slice(-1).pop(); //get name of final directory in the path
830
- var homepage = meta.contents[0].pages[0];
831
- var homepage = homepage.source.substr(0, homepage.source.lastIndexOf('.'))+'.html';
832
- var redirectLink = parent+'/'+homepage;
833
- $ = templates.redirect;
834
- $('a').attr('href', redirectLink);
835
- $('meta[http-equiv=REFRESH]').attr('content', '0;url='+redirectLink);
836
- var file = options.output+'../'+'index.html';
837
- try {
838
- fs.outputFileSync(file, $.html(), 'utf-8');
839
- } catch (error) {
840
- console.log(chalk.red('Error writing redirect file: '+file));
841
- if (options.verbose === true) {
842
- console.log(chalk.red(error));
843
- }
844
- //don't exit because redirect error is not a fatal error
845
- }
928
+ wkhtmltopdf.stdout.on('data', (data) => {
929
+ //do nothing
930
+ });
931
+
932
+ wkhtmltopdf.stderr.on('data', (data) => {
933
+ //do nothing
934
+ });
935
+
936
+ wkhtmltopdf.on('close', (code) => {
937
+ if (options.verbose !== true) {
938
+ spinner.stop();
939
+ console.log(''); //newline after spinner stops
940
+ }
941
+ if (code !== 0) {
942
+ let warning =
943
+ 'wkhtmltopdf exited with a warning or error: try the -v option for details';
944
+ console.log(chalk.yellow(warning));
945
+ }
946
+ cleanUp();
947
+ });
948
+ };
949
+
950
+ let createRedirect = () => {
951
+ if (options.redirect) {
952
+ let parent = options.output.replace(/\/$/, ''); //trim any trailing slash
953
+ parent = parent.split(path.sep).slice(-1).pop(); //get name of final directory in the path
954
+ let homepage = meta.contents[0].pages[0];
955
+ homepage =
956
+ homepage.source.substr(0, homepage.source.lastIndexOf('.')) + '.html';
957
+ let redirectLink = parent + '/' + homepage;
958
+ let $ = templates.redirect;
959
+ $('a').attr('href', redirectLink);
960
+ $('meta[http-equiv=REFRESH]').attr('content', '0;url=' + redirectLink);
961
+ let file = options.output + '../' + 'index.html';
962
+ try {
963
+ fs.outputFileSync(file, $.html(), 'utf-8');
964
+ } catch (error) {
965
+ console.log(chalk.red('Error writing redirect file: ' + file));
966
+ if (options.verbose === true) {
967
+ console.log(chalk.red(error));
846
968
  }
969
+ //don't exit because redirect error is not a fatal error
970
+ }
847
971
  }
972
+ };
848
973
 
849
- /*
850
- cleanup
851
- */
974
+ /*
975
+ cleanup
976
+ */
852
977
 
853
- var cleanUp = function () {
854
- createRedirect();
855
- //remove temp files
856
- if (options.pdf === true) {
857
- removeDirSync(options.output+'temp');
858
- }
859
- console.log(chalk.green.bold('Done!'));
978
+ let cleanUp = () => {
979
+ createRedirect();
980
+ //remove temp files
981
+ if (options.pdf === true) {
982
+ removeDirSync(options.output + 'temp');
860
983
  }
984
+ console.log(chalk.green.bold('Done!'));
985
+ };
861
986
  }
862
987
 
863
988
  module.exports = DocGen;