1 | var Q = require('q');
|
2 | var _ = require('lodash');
|
3 | var path = require('path');
|
4 | var parsers = require('gitbook-parsers');
|
5 |
|
6 | var fs = require('./utils/fs');
|
7 | var parseNavigation = require('./utils/navigation');
|
8 | var parseProgress = require('./utils/progress');
|
9 | var pageUtil = require('./utils/page');
|
10 | var pathUtil = require('./utils/path');
|
11 | var links = require('./utils/links');
|
12 | var i18n = require('./utils/i18n');
|
13 | var logger = require('./utils/logger');
|
14 |
|
15 | var Configuration = require('./configuration');
|
16 | var TemplateEngine = require('./template');
|
17 | var PluginsList = require('./pluginslist');
|
18 |
|
19 | var generators = require('./generators');
|
20 |
|
21 | var Book = function(root, context, parent) {
|
22 | this.context = _.defaults(context || {}, {
|
23 |
|
24 | config: {},
|
25 |
|
26 |
|
27 | log: function(msg) {
|
28 | process.stdout.write(msg);
|
29 | },
|
30 |
|
31 |
|
32 | logLevel: 'info'
|
33 | });
|
34 |
|
35 |
|
36 | this.log = logger(this.context.log, this.context.logLevel);
|
37 |
|
38 |
|
39 | this.root = path.resolve(root);
|
40 |
|
41 |
|
42 | this.parent = parent;
|
43 |
|
44 |
|
45 | this.config = new Configuration(this, this.context.config);
|
46 | Object.defineProperty(this, 'options', {
|
47 | get: function () {
|
48 | return this.config.options;
|
49 | }
|
50 | });
|
51 |
|
52 |
|
53 | this.template = new TemplateEngine(this);
|
54 |
|
55 |
|
56 | this.summary = {};
|
57 | this.navigation = [];
|
58 |
|
59 |
|
60 | this.glossary = [];
|
61 |
|
62 |
|
63 | this.langs = [];
|
64 |
|
65 |
|
66 | this.books = [];
|
67 |
|
68 |
|
69 | this.files = [];
|
70 |
|
71 |
|
72 | this.plugins = new PluginsList(this);
|
73 |
|
74 |
|
75 | this.summaryFile = null;
|
76 | this.glossaryFile = null;
|
77 | this.readmeFile = null;
|
78 | this.langsFile = null;
|
79 |
|
80 |
|
81 | _.bindAll(this);
|
82 | };
|
83 |
|
84 |
|
85 | Book.prototype.toString = function() {
|
86 | return '[Book '+this.root+']';
|
87 | };
|
88 |
|
89 |
|
90 | Book.prototype.parse = function() {
|
91 | var that = this;
|
92 | var multilingual = false;
|
93 |
|
94 | return this.parseConfig()
|
95 |
|
96 | .then(function() {
|
97 | return that.parsePlugins();
|
98 | })
|
99 |
|
100 | .then(function() {
|
101 | return that.parseLangs()
|
102 | .then(function() {
|
103 | multilingual = that.langs.length > 0;
|
104 | if (multilingual) that.log.info.ln('Parsing multilingual book, with', that.langs.length, 'languages');
|
105 |
|
106 |
|
107 | that.books = _.map(that.langs, function(lang) {
|
108 | that.log.info.ln('Preparing language book', lang.lang);
|
109 | return new Book(
|
110 | path.join(that.root, lang.path),
|
111 | _.merge({}, that.context, {
|
112 | config: _.extend({}, that.options, {
|
113 | 'output': path.join(that.options.output, lang.lang),
|
114 | 'language': lang.lang
|
115 | })
|
116 | }),
|
117 | that
|
118 | );
|
119 | });
|
120 | });
|
121 | })
|
122 |
|
123 | .then(function() {
|
124 | if (multilingual) return;
|
125 | return that.listAllFiles();
|
126 | })
|
127 | .then(function() {
|
128 | if (multilingual) return;
|
129 | return that.parseReadme();
|
130 | })
|
131 | .then(function() {
|
132 | if (multilingual) return;
|
133 | return that.parseSummary();
|
134 | })
|
135 | .then(function() {
|
136 | if (multilingual) return;
|
137 | return that.parseGlossary();
|
138 | })
|
139 |
|
140 | .then(function() {
|
141 |
|
142 | return _.reduce(that.books, function(prev, book) {
|
143 | return prev.then(function() {
|
144 | return book.parse();
|
145 | });
|
146 | }, Q());
|
147 | })
|
148 |
|
149 | .thenResolve(this);
|
150 | };
|
151 |
|
152 |
|
153 | Book.prototype.generate = function(generator) {
|
154 | var that = this;
|
155 | that.options.generator = generator || that.options.generator;
|
156 |
|
157 | that.log.info.ln('start generation with', that.options.generator, 'generator');
|
158 | return Q()
|
159 |
|
160 |
|
161 | .then(function() {
|
162 | that.log.info('clean', that.options.generator, 'generator');
|
163 | return fs.clean(that.options.output)
|
164 | .progress(function(p) {
|
165 | that.log.debug.ln('remove', p.file, '('+p.i+'/'+p.count+')');
|
166 | })
|
167 | .then(function() {
|
168 | that.log.info.ok();
|
169 | });
|
170 | })
|
171 |
|
172 |
|
173 | .then(function() {
|
174 | var Generator = generators[generator];
|
175 | if (!Generator) throw 'Generator \''+that.options.generator+'\' doesn\'t exist';
|
176 | generator = new Generator(that);
|
177 |
|
178 | return generator.prepare();
|
179 | })
|
180 |
|
181 |
|
182 | .then(function() {
|
183 | return that.callHook('config', that.config.dump())
|
184 | .then(function(newConfig) {
|
185 | that.config.replace(newConfig);
|
186 | });
|
187 | })
|
188 |
|
189 |
|
190 | .then(function() {
|
191 | if (that.isMultilingual()) {
|
192 | return that.generateMultiLingual(generator);
|
193 | } else {
|
194 |
|
195 | var ops = _.groupBy(that.files, function(file) {
|
196 | if (file[file.length -1] == '/') {
|
197 | return 'directories';
|
198 | } else if (_.contains(parsers.extensions, path.extname(file)) && that.navigation[file]) {
|
199 | return 'content';
|
200 | } else {
|
201 | return 'files';
|
202 | }
|
203 | });
|
204 |
|
205 |
|
206 | return Q()
|
207 |
|
208 |
|
209 | .then(function() {
|
210 | return _.reduce(ops.directories || [], function(prev, folder) {
|
211 | return prev.then(function() {
|
212 | that.log.debug.ln('transferring folder', folder);
|
213 | return Q(generator.transferFolder(folder));
|
214 | });
|
215 | }, Q());
|
216 | })
|
217 |
|
218 |
|
219 | .then(function() {
|
220 | return Q.all(_.map(ops.files || [], function(file) {
|
221 | that.log.debug.ln('transferring file', file);
|
222 | return Q(generator.transferFile(file));
|
223 | }));
|
224 | })
|
225 |
|
226 |
|
227 | .then(function() {
|
228 | var nFiles = (ops.content || []).length;
|
229 | return _.reduce(ops.content || [], function(prev, file, i) {
|
230 | return prev.then(function() {
|
231 | var p = ((i*100)/nFiles).toFixed(0)+'%';
|
232 | that.log.debug.ln('processing', file, p);
|
233 |
|
234 | return Q(generator.convertFile(file))
|
235 | .fail(function(err) {
|
236 |
|
237 | throw that.normError(err, {
|
238 | fileName: file
|
239 | });
|
240 | });
|
241 | });
|
242 | }, Q());
|
243 | });
|
244 | }
|
245 | })
|
246 |
|
247 |
|
248 | .then(function() {
|
249 | return that.callHook('finish:before');
|
250 | })
|
251 | .then(function() {
|
252 | return generator.finish();
|
253 | })
|
254 | .then(function() {
|
255 | return that.callHook('finish');
|
256 | })
|
257 | .then(function() {
|
258 | that.log.info.ln('generation is finished');
|
259 | });
|
260 | };
|
261 |
|
262 |
|
263 | Book.prototype.generateMultiLingual = function() {
|
264 | var that = this;
|
265 |
|
266 | return Q()
|
267 | .then(function() {
|
268 |
|
269 | return _.reduce(that.books, function(prev, book) {
|
270 | return prev.then(function() {
|
271 | return book.generate(that.options.generator);
|
272 | });
|
273 | }, Q());
|
274 | });
|
275 | };
|
276 |
|
277 |
|
278 | Book.prototype.generateFile = function(output, options) {
|
279 | var book = this;
|
280 |
|
281 | options = _.defaults(options || {}, {
|
282 | ebookFormat: path.extname(output).slice(1)
|
283 | });
|
284 | output = output || path.resolve(book.root, 'book.'+options.ebookFormat);
|
285 |
|
286 | return fs.tmp.dir()
|
287 | .then(function(tmpDir) {
|
288 | book.setOutput(tmpDir);
|
289 |
|
290 | return book.generate(options.ebookFormat)
|
291 | .then(function() {
|
292 | var copyFile = function(lang) {
|
293 | var _outputFile = output;
|
294 | var _tmpDir = tmpDir;
|
295 |
|
296 | if (lang) {
|
297 | _outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+'_'+lang+path.extname(_outputFile);
|
298 | _tmpDir = path.join(_tmpDir, lang);
|
299 | }
|
300 |
|
301 | book.log.debug.ln('copy ebook to', _outputFile);
|
302 | return fs.copy(
|
303 | path.join(_tmpDir, 'index.'+options.ebookFormat),
|
304 | _outputFile
|
305 | );
|
306 | };
|
307 |
|
308 |
|
309 | return Q()
|
310 | .then(function() {
|
311 | if (book.isMultilingual()) {
|
312 | return Q.all(
|
313 | _.map(book.langs, function(lang) {
|
314 | return copyFile(lang.lang);
|
315 | })
|
316 | )
|
317 | .thenResolve(book.langs.length);
|
318 | } else {
|
319 | return copyFile().thenResolve(1);
|
320 | }
|
321 | })
|
322 | .then(function(n) {
|
323 | book.log.info.ok(n+' file(s) generated');
|
324 |
|
325 | return fs.remove(tmpDir);
|
326 | });
|
327 | });
|
328 | });
|
329 | };
|
330 |
|
331 |
|
332 | Book.prototype.parseConfig = function() {
|
333 | var that = this;
|
334 |
|
335 | that.log.info('loading book configuration....');
|
336 | return that.config.load()
|
337 | .then(function() {
|
338 | that.log.info.ok();
|
339 | });
|
340 | };
|
341 |
|
342 |
|
343 | Book.prototype.parsePlugins = function() {
|
344 | var that = this;
|
345 |
|
346 |
|
347 | return that.plugins.load(that.options.plugins)
|
348 | .then(function() {
|
349 | if (_.size(that.plugins.failed) > 0) return Q.reject(new Error('Error loading plugins: '+that.plugins.failed.join(',')+'. Run \'gitbook install\' to install plugins from NPM.'));
|
350 |
|
351 | that.log.info.ok(that.plugins.count()+' plugins loaded');
|
352 | that.log.debug.ln('normalize plugins list');
|
353 | });
|
354 | };
|
355 |
|
356 |
|
357 | Book.prototype.parseReadme = function() {
|
358 | var that = this;
|
359 | var structure = that.config.getStructure('readme');
|
360 | that.log.debug.ln('start parsing readme:', structure);
|
361 |
|
362 | return that.findFile(structure)
|
363 | .then(function(readme) {
|
364 | if (!readme) throw 'No README file';
|
365 | if (!_.contains(that.files, readme.path)) throw 'README file is ignored';
|
366 |
|
367 | that.readmeFile = readme.path;
|
368 | that._defaultsStructure(that.readmeFile);
|
369 |
|
370 | that.log.debug.ln('readme located at', that.readmeFile);
|
371 | return that.template.renderFile(that.readmeFile)
|
372 | .then(function(content) {
|
373 | return readme.parser.readme(content)
|
374 | .fail(function(err) {
|
375 | throw that.normError(err, {
|
376 | name: err.name || 'Readme Parse Error',
|
377 | fileName: that.readmeFile
|
378 | });
|
379 | });
|
380 | });
|
381 | })
|
382 | .then(function(readme) {
|
383 | that.options.title = that.options.title || readme.title;
|
384 | that.options.description = that.options.description || readme.description;
|
385 | });
|
386 | };
|
387 |
|
388 |
|
389 |
|
390 | Book.prototype.parseLangs = function() {
|
391 | var that = this;
|
392 |
|
393 | var structure = that.config.getStructure('langs');
|
394 | that.log.debug.ln('start parsing languages index:', structure);
|
395 |
|
396 | return that.findFile(structure)
|
397 | .then(function(langs) {
|
398 | if (!langs) return [];
|
399 |
|
400 | that.langsFile = langs.path;
|
401 | that._defaultsStructure(that.langsFile);
|
402 |
|
403 | that.log.debug.ln('languages index located at', that.langsFile);
|
404 | return that.template.renderFile(that.langsFile)
|
405 | .then(function(content) {
|
406 | return langs.parser.langs(content)
|
407 | .fail(function(err) {
|
408 | throw that.normError(err, {
|
409 | name: err.name || 'Langs Parse Error',
|
410 | fileName: that.langsFile
|
411 | });
|
412 | });
|
413 | });
|
414 | })
|
415 | .then(function(langs) {
|
416 | that.langs = langs;
|
417 | });
|
418 | };
|
419 |
|
420 |
|
421 | Book.prototype.parseSummary = function() {
|
422 | var that = this;
|
423 |
|
424 | var structure = that.config.getStructure('summary');
|
425 | that.log.debug.ln('start parsing summary:', structure);
|
426 |
|
427 | return that.findFile(structure)
|
428 | .then(function(summary) {
|
429 | if (!summary) throw 'No SUMMARY file';
|
430 |
|
431 |
|
432 | that.summaryFile = summary.path;
|
433 | that._defaultsStructure(that.summaryFile);
|
434 | that.files = _.without(that.files, that.summaryFile);
|
435 |
|
436 | that.log.debug.ln('summary located at', that.summaryFile);
|
437 | return that.template.renderFile(that.summaryFile)
|
438 | .then(function(content) {
|
439 | return summary.parser.summary(content, {
|
440 | entryPoint: that.readmeFile,
|
441 | entryPointTitle: that.i18n('SUMMARY_INTRODUCTION'),
|
442 | files: that.files
|
443 | })
|
444 | .fail(function(err) {
|
445 | throw that.normError(err, {
|
446 | name: err.name || 'Summary Parse Error',
|
447 | fileName: that.summaryFile
|
448 | });
|
449 | });
|
450 | });
|
451 | })
|
452 | .then(function(summary) {
|
453 | that.summary = summary;
|
454 | that.navigation = parseNavigation(that.summary, that.files);
|
455 | });
|
456 | };
|
457 |
|
458 |
|
459 | Book.prototype.parseGlossary = function() {
|
460 | var that = this;
|
461 |
|
462 | var structure = that.config.getStructure('glossary');
|
463 | that.log.debug.ln('start parsing glossary: ', structure);
|
464 |
|
465 | return that.findFile(structure)
|
466 | .then(function(glossary) {
|
467 | if (!glossary) return [];
|
468 |
|
469 |
|
470 | that.glossaryFile = glossary.path;
|
471 | that._defaultsStructure(that.glossaryFile);
|
472 | that.files = _.without(that.files, that.glossaryFile);
|
473 |
|
474 | that.log.debug.ln('glossary located at', that.glossaryFile);
|
475 | return that.template.renderFile(that.glossaryFile)
|
476 | .then(function(content) {
|
477 | return glossary.parser.glossary(content)
|
478 | .fail(function(err) {
|
479 | throw that.normError(err, {
|
480 | name: err.name || 'Glossary Parse Error',
|
481 | fileName: that.glossaryFile
|
482 | });
|
483 | });
|
484 | });
|
485 | })
|
486 | .then(function(glossary) {
|
487 | that.glossary = glossary;
|
488 | });
|
489 | };
|
490 |
|
491 |
|
492 | Book.prototype.parsePage = function(filename, options) {
|
493 | var that = this, page = {};
|
494 | options = _.defaults(options || {}, {
|
495 |
|
496 | convertImages: false,
|
497 |
|
498 |
|
499 | interpolateTemplate: _.identity,
|
500 |
|
501 |
|
502 | interpolateContent: _.identity
|
503 | });
|
504 |
|
505 | var interpolate = function(fn) {
|
506 | return Q(fn(page))
|
507 | .then(function(_page) {
|
508 | page = _page || page;
|
509 | });
|
510 | };
|
511 |
|
512 | that.log.debug.ln('start parsing file', filename);
|
513 |
|
514 | var extension = path.extname(filename);
|
515 | var filetype = parsers.get(extension);
|
516 |
|
517 | if (!filetype) return Q.reject(new Error('Can\'t parse file: '+filename));
|
518 |
|
519 |
|
520 | page.type = filetype.name;
|
521 |
|
522 |
|
523 | page.path = filename;
|
524 |
|
525 |
|
526 | page.rawPath = path.resolve(that.root, filename);
|
527 |
|
528 |
|
529 | page.progress = parseProgress(that.navigation, filename);
|
530 |
|
531 | that.log.debug.ln('render template', filename);
|
532 |
|
533 |
|
534 | return that.readFile(page.path)
|
535 | .then(function(content) {
|
536 | page.content = content;
|
537 |
|
538 | return interpolate(options.interpolateTemplate);
|
539 | })
|
540 |
|
541 |
|
542 | .then(function() {
|
543 | return filetype.page.prepare(page.content)
|
544 | .then(function(content) {
|
545 | page.content = content;
|
546 | });
|
547 | })
|
548 |
|
549 |
|
550 | .then(function() {
|
551 | return that.template.renderPage(page);
|
552 | })
|
553 |
|
554 |
|
555 | .then(function(content) {
|
556 | page.content = content;
|
557 |
|
558 | that.log.debug.ln('use file parser', filetype.name, 'for', filename);
|
559 | return filetype.page(page.content);
|
560 | })
|
561 |
|
562 |
|
563 | .then(function(_page) {
|
564 | return _.reduce(_page.sections, function(prev, section) {
|
565 | return prev.then(function(_sections) {
|
566 | return that.template.postProcess(section.content || '')
|
567 | .then(function(content) {
|
568 | section.content = content;
|
569 | return _sections.concat([section]);
|
570 | });
|
571 | });
|
572 | }, Q([]));
|
573 | })
|
574 |
|
575 |
|
576 | .then(function(_sections) {
|
577 | return pageUtil.normalize(_sections, {
|
578 | book: that,
|
579 | convertImages: options.convertImages,
|
580 | input: filename,
|
581 | navigation: that.navigation,
|
582 | base: path.dirname(filename) || './',
|
583 | output: path.dirname(filename) || './',
|
584 | glossary: that.glossary
|
585 | });
|
586 | })
|
587 |
|
588 |
|
589 | .then(function(_sections) {
|
590 | page.sections = _sections;
|
591 | return interpolate(options.interpolateContent);
|
592 | })
|
593 |
|
594 | .then(function() {
|
595 | return page;
|
596 | });
|
597 | };
|
598 |
|
599 |
|
600 | Book.prototype.findFile = function(filename) {
|
601 | var that = this;
|
602 |
|
603 | return _.reduce(parsers.extensions, function(prev, ext) {
|
604 | return prev.then(function(output) {
|
605 |
|
606 | if (output) return output;
|
607 |
|
608 | var filepath = filename+ext;
|
609 |
|
610 | return that.fileExists(filepath)
|
611 | .then(function(exists) {
|
612 | if (!exists) return null;
|
613 | return {
|
614 | parser: parsers.get(ext),
|
615 | path: filepath
|
616 | };
|
617 | });
|
618 | });
|
619 | }, Q(null));
|
620 | };
|
621 |
|
622 |
|
623 | Book.prototype.formatString = function(extension, content) {
|
624 | return Q()
|
625 | .then(function() {
|
626 | var filetype = parsers.get(extension);
|
627 | if (!filetype) throw new Error('Filetype doesn\'t exist: '+filetype);
|
628 |
|
629 | return filetype.page(content);
|
630 | })
|
631 |
|
632 |
|
633 | .then(function(page) {
|
634 | return _.reduce(page.sections, function(content, section) {
|
635 | return content + section.content;
|
636 | }, '');
|
637 | });
|
638 | };
|
639 |
|
640 |
|
641 | Book.prototype.fileExists = function(filename) {
|
642 | return fs.exists(
|
643 | this.resolve(filename)
|
644 | );
|
645 | };
|
646 |
|
647 |
|
648 | Book.prototype.fileIsInBook = function(filename) {
|
649 | return pathUtil.isInRoot(this.root, filename);
|
650 | };
|
651 |
|
652 |
|
653 | Book.prototype.readFile = function(filename) {
|
654 | return fs.readFile(
|
655 | this.resolve(filename),
|
656 | { encoding: 'utf8' }
|
657 | );
|
658 | };
|
659 |
|
660 |
|
661 | Book.prototype.statFile = function(filename) {
|
662 | return fs.stat(this.resolve(filename));
|
663 | };
|
664 |
|
665 |
|
666 | Book.prototype.listAllFiles = function() {
|
667 | var that = this;
|
668 |
|
669 | return fs.list(this.root, {
|
670 | ignoreFiles: ['.ignore', '.gitignore', '.bookignore'],
|
671 | ignoreRules: [
|
672 |
|
673 | '.git/',
|
674 | '.gitignore',
|
675 |
|
676 |
|
677 | '.DS_Store',
|
678 |
|
679 |
|
680 | 'node_modules',
|
681 |
|
682 |
|
683 | '_book',
|
684 | '*.pdf',
|
685 | '*.epub',
|
686 | '*.mobi',
|
687 |
|
688 |
|
689 | '.ignore',
|
690 | '.bookignore',
|
691 | 'book.json',
|
692 | ]
|
693 | })
|
694 | .then(function(_files) {
|
695 | that.files = _files;
|
696 | });
|
697 | };
|
698 |
|
699 |
|
700 | Book.prototype.isMultilingual = function() {
|
701 | return this.books.length > 0;
|
702 | };
|
703 |
|
704 |
|
705 | Book.prototype.parentRoot = function() {
|
706 | if (this.parent) return this.parent.parentRoot();
|
707 | return this.root;
|
708 | };
|
709 |
|
710 |
|
711 | Book.prototype.isSubBook = function() {
|
712 | return !!this.parent;
|
713 | };
|
714 |
|
715 |
|
716 | Book.prototype.isEntryPoint = function(fp) {
|
717 | return fp == this.readmeFile;
|
718 | };
|
719 |
|
720 |
|
721 | Book.prototype.getConfig = function(key, def) {
|
722 | return this.config.get(key, def);
|
723 | };
|
724 |
|
725 |
|
726 |
|
727 | Book.prototype.resolve = function() {
|
728 | return pathUtil.resolveInRoot.apply(null, [this.root].concat(_.toArray(arguments)));
|
729 | };
|
730 |
|
731 |
|
732 | Book.prototype.relative = function(p) {
|
733 | return path.relative(this.root, p);
|
734 | };
|
735 |
|
736 |
|
737 | Book.prototype.contentPath = function(link) {
|
738 | if (
|
739 | path.basename(link, path.extname(link)) == 'README' ||
|
740 | link == this.readmeFile
|
741 | ) {
|
742 | link = path.join(path.dirname(link), 'index'+path.extname(link));
|
743 | }
|
744 |
|
745 | link = links.changeExtension(link, '.html');
|
746 | return link;
|
747 | };
|
748 |
|
749 |
|
750 | Book.prototype.contentLink = function(link) {
|
751 | return links.normalize(this.contentPath(link));
|
752 | };
|
753 |
|
754 |
|
755 | Book.prototype._defaultsStructure = function(filename) {
|
756 | var that = this;
|
757 | var extension = path.extname(filename);
|
758 |
|
759 | that.readmeFile = that.readmeFile || that.config.getStructure('readme')+extension;
|
760 | that.summaryFile = that.summaryFile || that.config.getStructure('summary')+extension;
|
761 | that.glossaryFile = that.glossaryFile || that.config.getStructure('glossary')+extension;
|
762 | that.langsFile = that.langsFile || that.config.getStructure('langs')+extension;
|
763 | };
|
764 |
|
765 |
|
766 | Book.prototype.setOutput = function(p) {
|
767 | var that = this;
|
768 | this.options.output = path.resolve(p);
|
769 |
|
770 | _.each(this.books, function(book) {
|
771 | book.setOutput(path.join(that.options.output, book.options.language));
|
772 | });
|
773 | };
|
774 |
|
775 |
|
776 | Book.prototype.i18n = function() {
|
777 | var args = Array.prototype.slice.call(arguments);
|
778 | return i18n.__.apply({}, [this.config.normalizeLanguage()].concat(args));
|
779 | };
|
780 |
|
781 |
|
782 | Book.prototype.normError = function(err, opts, defs) {
|
783 | if (_.isString(err)) err = new Error(err);
|
784 |
|
785 |
|
786 | _.extend(err, opts || {});
|
787 | _.defaults(err, defs || {});
|
788 |
|
789 | err.lineNumber = err.lineNumber || err.lineno;
|
790 | err.columnNumber = err.columnNumber || err.colno;
|
791 |
|
792 | err.toString = function() {
|
793 | var attributes = [];
|
794 |
|
795 | if (this.fileName) attributes.push('In file \''+this.fileName+'\'');
|
796 | if (this.lineNumber) attributes.push('Line '+this.lineNumber);
|
797 | if (this.columnNumber) attributes.push('Column '+this.columnNumber);
|
798 | return (this.name || 'Error')+': '+this.message+((attributes.length > 0)? ' ('+attributes.join(', ')+')' : '');
|
799 | };
|
800 |
|
801 | return err;
|
802 | };
|
803 |
|
804 |
|
805 | Book.prototype.callHook = function(name, data) {
|
806 | return this.plugins.hook(name, data);
|
807 | };
|
808 |
|
809 | module.exports= Book;
|