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