import util from 'util';
import * as utils from './utils.js';
import path from 'path';
import xmldom from '@xmldom/xmldom';
import { Manifest, ManifestItem, tocData as manifest_tocData } from './manifest';
import { promises as fsp /*, default as fs */ } from 'fs';
import { Configuration } from './Configuration.js';
export function findMetadataInOPF(OPFXML) {
for (const elem of utils.nodeList2Array(OPFXML.getElementsByTagName("metadata")).concat(utils.nodeList2Array(OPFXML.getElementsByTagName("opf:metadata")))) {
if (elem.nodeName.toUpperCase() === 'metadata'.toUpperCase()
|| elem.nodeName.toUpperCase() === 'opf:metadata'.toUpperCase()) {
return elem;
}
}
return undefined;
}
export function findManifestInOPF(OPFXML) {
for (const elem of utils.nodeList2Array(OPFXML.getElementsByTagName("manifest")).concat(utils.nodeList2Array(OPFXML.getElementsByTagName("opf:manifest")))) {
if (elem.nodeName.toUpperCase() === 'manifest'.toUpperCase()
|| elem.nodeName.toUpperCase() === 'opf:manifest'.toUpperCase()) {
return elem;
}
}
return undefined;
}
export function findSpineInOPF(OPFXML) {
for (const elem of utils.nodeList2Array(OPFXML.getElementsByTagName("spine")).concat(utils.nodeList2Array(OPFXML.getElementsByTagName("opf:spine")))) {
if (elem.nodeName.toUpperCase() === 'spine'.toUpperCase()
|| elem.nodeName.toUpperCase() === 'opf:spine'.toUpperCase()) {
return elem;
}
}
return undefined;
}
export function refines(metadata, id) {
const ret = [];
for (const meta of utils.nodeList2Array(metadata.getElementsByTagName("meta")).concat(utils.nodeList2Array(metadata.getElementsByTagName("opf:meta")))) {
const refines = meta.getAttribute('refines');
if (refines && refines === `#${id}`) {
ret.push(meta);
}
}
return ret;
}
export function titles(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
const ret = [];
for (const title of utils.nodeListIterator(
metadata.getElementsByTagName('dc:title')
)) {
ret.push({
id: title.getAttribute('id'),
title: title.textContent
});
}
for (const title of ret) {
const refines = exports.refines(metadata, title.id);
if (refines && refines.length > 0) {
for (const refine of refines) {
const property = refine.getAttribute('property');
if (property === 'title-type') {
title.titleType = refine.textContent;
} else if (property === 'display-seq') {
title.displaySequence = refine.textContent;
} else if (property === 'file-as') {
title.fileAs = refine.textContent;
}
}
}
}
return ret;
}
export function identifiers(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
// Get the ID indicating Unique Identifier
const uniqueIDname = OPFXML.documentElement.getAttribute('unique-identifier');
const ret = [];
for (const identifier of utils.nodeListIterator(
metadata.getElementsByTagName('dc:identifier')
)) {
const idIdentifier = identifier.getAttribute('id');
// Properly match that this is the Unique Identifier
const isUnique = idIdentifier && idIdentifier === uniqueIDname
? true : false;
const newIdentifier = {
id: undefined,
type: undefined,
unique: isUnique,
string: identifier.textContent,
identifierTypeScheme: undefined,
identifierType: undefined
};
if (idIdentifier && idIdentifier !== '') {
newIdentifier.id = idIdentifier;
}
// remove prefixes from identifier for identified types
if (newIdentifier.string.indexOf('urn:isbn:') === 0) {
newIdentifier.type = 'isbn';
newIdentifier.string = newIdentifier.string.slice('urn:isbn:'.length);
} else if (newIdentifier.string.indexOf('urn:uuid:') === 0) {
newIdentifier.type = 'uuid';
newIdentifier.string = newIdentifier.string.slice('urn:uuid:'.length);
} else if (newIdentifier.string.indexOf('urn:') === 0) {
newIdentifier.type = 'urn';
}
if (idIdentifier) {
const refines = exports.refines(metadata, idIdentifier);
for (const refine of refines) {
const prop = refine.getAttribute('property');
if (prop === 'identifier-type') {
newIdentifier.identifierTypeScheme = refine.getAttribute('scheme');
newIdentifier.identifierType = refine.textContent;
}
}
}
ret.push(newIdentifier);
}
// console.log(`identifiers ${util.inspect(ret)}`);
return ret;
}
export function languages(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
const ret = [];
for (const language of utils.nodeListIterator(
metadata.getElementsByTagName('dc:language')
)) {
ret.push({
id: language.getAttribute('id'),
langcode: language.textContent
});
}
return ret;
}
export function creators(OPFXML, tag) {
const metadata = exports.findMetadataInOPF(OPFXML);
const ret = [];
for (const creator of utils.nodeListIterator(
metadata.getElementsByTagName(tag)
)) {
let theID = creator.getAttribute('id');
if (!theID || theID === '') {
theID = undefined;
}
const item = {
id: undefined,
name: creator.textContent,
role: undefined,
roleSchema: undefined,
fileAs: undefined
};
if (theID) item.id = theID;
if (item.id) {
const refines = exports.refines(metadata, item.id);
for (const refine of refines) {
const property = refine.getAttribute('property');
if (property && property === 'role') {
item.role = refine.textContent;
item.roleSchema = refine.getAttribute('schema');
}
if (property && property === 'file-as') {
item.fileAs = refine.textContent;
}
}
}
ret.push(item);
}
return ret;
}
export function publicationDate(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
for (const date of utils.nodeListIterator(
metadata.getElementsByTagName('dc:date')
)) {
return date.textContent;
}
return "";
}
export function modifiedDate(OPFXML) {
// const metadata = exports.findMetadataInOPF(OPFXML);
for (const meta of utils.nodeList2Array(OPFXML.getElementsByTagName("meta"))
.concat(utils.nodeList2Array(OPFXML.getElementsByTagName("opf:meta")))) {
const property = meta.getAttribute('property');
if (property && property === 'dcterms:modified') {
return meta.textContent;
}
}
return "";
}
export function subjects(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
const ret = [];
for (const subject of utils.nodeListIterator(
metadata.getElementsByTagName('dc:subject')
)) {
ret.push(subject.textContent);
}
return ret;
}
export function description(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
for (const description of utils.nodeListIterator(
metadata.getElementsByTagName('dc:description')
)) {
return description.textContent;
}
return "";
}
export function format(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
for (const format of utils.nodeListIterator(
metadata.getElementsByTagName('dc:format')
)) {
return format.textContent;
}
return "";
}
export function publisher(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
for (const publisher of utils.nodeListIterator(
metadata.getElementsByTagName('dc:publisher')
)) {
return publisher.textContent;
}
return "";
}
export function relation(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
for (const relation of utils.nodeListIterator(
metadata.getElementsByTagName('dc:relation')
)) {
return relation.textContent;
}
return "";
}
export function coverage(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
for (const coverage of utils.nodeListIterator(
metadata.getElementsByTagName('dc:coverage')
)) {
return coverage.textContent;
}
return "";
}
export function rights(OPFXML) {
const metadata = exports.findMetadataInOPF(OPFXML);
for (const rights of utils.nodeListIterator(
metadata.getElementsByTagName('dc:rights')
)) {
return rights.textContent;
}
return "";
}
export function manifest(config, OPFXML) {
const manifest = exports.findManifestInOPF(OPFXML);
const ret = new Manifest([]);
for (const item of utils.nodeList2Array(manifest.getElementsByTagName("item"))
.concat(utils.nodeList2Array(manifest.getElementsByTagName("opf:item")))) {
const datum = new ManifestItem({
id: item.getAttribute('id'),
mime: item.getAttribute('media-type'),
mimeoverride: "",
basedir: config.renderedFullPath,
path: item.getAttribute('href'),
dirname: path.dirname(item.getAttribute('href')),
filename: path.basename(item.getAttribute('href')),
suppressOPF: false,
in_spine: false,
spine_order: false
});
const properties = item.getAttribute('properties');
if (properties) datum.properties = properties;
if (properties && properties.indexOf('nav') >= 0) {
datum.is_nav = true;
config.sourceBookTOCID = datum.id;
config.sourceBookTOCHREF = datum.path;
} else {
datum.is_nav = false;
}
if (properties && properties.indexOf('cover-image') >= 0) {
datum.is_cover_image = true;
config.sourceBookCoverID = datum.id;
config.sourceBookCoverHREF = datum.path;
} else {
datum.is_cover_image = false;
}
if (properties && properties.indexOf('mathml') >= 0) {
datum.is_mathml = true;
} else {
datum.is_mathml = false;
}
if (properties && properties.indexOf('scripted') >= 0) {
datum.is_scripted = true;
} else {
datum.is_scripted = false;
}
if (properties && properties.indexOf('svg') >= 0) {
datum.is_svg = true;
} else {
datum.is_svg = false;
}
if (properties && properties.indexOf('remote-resources') >= 0) {
datum.is_remote_resources = true;
} else {
datum.is_remote_resources = false;
}
if (properties && properties.indexOf('switch') >= 0) {
datum.is_switch = true;
} else {
datum.is_switch = false;
}
ret.addItem(datum);
}
// NOTE: This ignores the possibility of 'toc="NCX"'
const spine = exports.findSpineInOPF(OPFXML);
let spine_order = 0;
for (const itemref of utils.nodeListIterator(spine.getElementsByTagName('itemref'))) {
const idref = itemref.getAttribute('idref');
for (const datum of ret) {
if (datum.id === idref) {
datum.in_spine = true;
datum.spine_order = spine_order++;
const linear = spine.getAttribute('linear');
if (linear && linear === 'no') {
datum.linear = 'no';
} else if (linear && linear === 'yes') {
datum.linear = 'yes';
}
}
}
}
return ret;
}
export async function readOpf(opfFN) {
const opfTXT = await fsp.readFile(opfFN, 'utf8');
const OPFXML = new xmldom.DOMParser().parseFromString(opfTXT);
return OPFXML;
}
// exports.makeOpfXml = async function(bookYaml, manifest, opfspine) {
export async function makeOpfXml(config) {
// TODO this will require different templates for EPUB versions
const uniqueIDname = "epub-unique-identifier";
const OPFXML = new xmldom.DOMParser().parseFromString(
`
`, 'text/xml');
const metadata = exports.findMetadataInOPF(OPFXML);
const manifestElem = exports.findManifestInOPF(OPFXML);
const spine = exports.findSpineInOPF(OPFXML);
let elem;
// Check for required parameters
if (typeof config.opfTitles === 'undefined'
|| config.opfTitles === null
|| config.opfTitles.length <= 0) {
throw new Error('no title');
}
if (typeof config.opfLanguages === 'undefined'
|| config.opfLanguages === null) {
throw new Error('no pubLanguage');
}
if (typeof config.opfPublicationDate == 'undefined' // TODO
|| config.opfPublicationDate === null) {
throw new Error('no dates');
}
// Identifiers
if (typeof config.opfIdentifiers !== 'undefined'
&& config.opfIdentifiers !== null) {
for (const identifier of config.opfIdentifiers) {
// console.log(`opfIdentifiers identifier ${util.inspect(identifier)}`);
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:identifier');
if (typeof identifier.unique !== 'undefined'
&& identifier.unique !== null) {
elem.setAttribute('id', uniqueIDname);
}
if (typeof identifier.type && identifier.type === "urn") {
elem.appendChild(OPFXML.createTextNode(identifier.string));
metadata.appendChild(elem);
} else if (typeof identifier.type && identifier.type === "isbn") {
elem.appendChild(
OPFXML.createTextNode(`urn:isbn:${identifier.string}`));
metadata.appendChild(elem);
} else if (typeof identifier.type && identifier.type === "uuid") {
elem.appendChild(
OPFXML.createTextNode(`urn:uuid:${identifier.string}`));
metadata.appendChild(elem);
} else {
throw new Error(`identifier with no type ${util.inspect(identifier)}`);
}
// TODO Format for other ID formats like ISBN
if (identifier.identifierType && identifier.identifierTypeScheme) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'meta');
elem.setAttribute('refines', `#${identifier.id}`);
elem.setAttribute('property', 'identifier-type');
elem.setAttribute('scheme', identifier.identifierTypeScheme);
elem.appendChild(OPFXML.createTextNode(identifier.identifierType));
metadata.appendChild(elem);
}
}
}
// <%= source %>
if (config.bookMetaSourceType && config.bookMetaSourceID) {
elem = OPFXML.createElementNS("http://purl.org/dc/elements/1.1/", 'dc:source');
if (config.bookMetaSourceType === "urn") {
elem.appendChild(OPFXML.createTextNode(config.bookMetaSourceID));
metadata.appendChild(elem);
} else if (config.bookMetaSourceType === "isbn") {
elem.appendChild(
OPFXML.createTextNode(`urn:isbn:${config.bookMetaSourceID}`));
metadata.appendChild(elem);
} else if (config.bookMetaSourceType === "uuid") {
elem.appendChild(
OPFXML.createTextNode(`urn:uuid:${config.bookMetaSourceID}`));
metadata.appendChild(elem);
} else {
throw new Error(`bookMetaSourceType with no type ${util.inspect(config.bookMetaSourceType)}`);
}
}
// Book title
// <%= title %>
if (typeof config.opfTitles !== undefined
&& config.opfTitles) {
let titleNum = 0;
for (const title of config.opfTitles) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:title');
if (title.id && title.id !== '') elem.setAttribute('id', title.id);
else elem.setAttribute('id', `title${titleNum++}`);
elem.appendChild(OPFXML.createTextNode(title.title));
metadata.appendChild(elem);
if (title.titleType) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'meta');
elem.setAttribute('refines', `#${title.id}`);
elem.setAttribute('property', 'title-type');
elem.appendChild(OPFXML.createTextNode(title.titleType));
metadata.appendChild(elem);
}
if (title.displaySequence) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'meta');
elem.setAttribute('refines', `#${title.id}`);
elem.setAttribute('property', 'display-seq');
elem.appendChild(OPFXML.createTextNode(title.displaySequence));
metadata.appendChild(elem);
}
if (title.fileAs) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'meta');
elem.setAttribute('refines', `#${title.id}`);
elem.setAttribute('property', 'file-as');
elem.appendChild(OPFXML.createTextNode(title.fileAs));
metadata.appendChild(elem);
}
}
}
// Book languages
// <%= language %>
//
if (typeof config.opfLanguages !== undefined
&& config.opfLanguages) {
for (const language of config.opfLanguages) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:language');
if (language.id) {
elem.setAttribute('id', language.id);
}
elem.appendChild(OPFXML.createTextNode(language.langcode));
metadata.appendChild(elem);
}
}
// ><%= creator.name %><%
// if (creator.role) { %>
// <%= creator.role %>
// <% }
let creatorNum = 0;
const mkCreatorContributor = function(OPFXML, parent, tag, obj) {
const elem = OPFXML.createElementNS("http://purl.org/dc/elements/1.1/", tag);
if (!obj.id || obj.id === '') obj.id = `creator${creatorNum++}`;
elem.setAttribute('id', obj.id);
elem.appendChild(OPFXML.createTextNode(obj.name));
// <%= creator.role %>
const metaelem = OPFXML.createElement('meta');
if (obj.role) {
metaelem.setAttribute('refines', "#"+ obj.id);
metaelem.setAttribute('property', "role");
// This attribute is shown in Tools For Change, but
// EPUBCheck complains if it is present.
// metaelem.setAttribute('schema', "marc:relators");
metaelem.appendChild(OPFXML.createTextNode(obj.role));
parent.appendChild(metaelem);
}
if (obj.fileAs) {
metaelem.setAttribute('refines', "#"+ obj.id);
metaelem.setAttribute('property', "file-as");
metaelem.appendChild(OPFXML.createTextNode(obj.fileAs));
parent.appendChild(metaelem);
}
return elem;
};
if (config.opfCreators) {
for (const creator of config.opfCreators) {
elem = mkCreatorContributor(OPFXML, metadata, 'dc:creator', creator);
metadata.appendChild(elem);
}
}
// opf:file-as="<%= contributor.fileAs %>"<% }
// if (contributor.role) { %> opf:role="<%= contributor.role %>"<% }
// %> ><%= contributor.name %>
if (config.opfContributors) {
for (const contributor of config.opfContributors) {
elem = mkCreatorContributor(OPFXML, metadata, 'dc:contributor', contributor);
metadata.appendChild(elem);
}
}
// <%= date %>
if (typeof config.opfPublicationDate !== 'undefined'
&& config.opfPublicationDate) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:date');
const date = utils.w3cdate(new Date(config.opfPublicationDate));
elem.appendChild(OPFXML.createTextNode(date));
metadata.appendChild(elem);
}
// <%= subject %>
for (const subject of config.opfSubjects) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:subject');
elem.appendChild(OPFXML.createTextNode(subject));
metadata.appendChild(elem);
}
// <%= description %>
if (typeof config.opfDescription !== 'undefined'
&& config.opfDescription) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'description');
elem.appendChild(OPFXML.createTextNode(config.description));
metadata.appendChild(elem);
}
// <%= modified %>
if (typeof config.opfModifiedDate !== 'undefined'
&& config.opfModifiedDate) {
elem = OPFXML.createElement('meta');
elem.setAttribute('property', "dcterms:modified");
const mdate = new Date(config.opfModifiedDate);
if (mdate) {
const date = utils.w3cdate(mdate);
elem.appendChild(OPFXML.createTextNode(date));
metadata.appendChild(elem);
}
}
// <%= format %>
if (typeof config.opfFormat !== 'undefined'
&& config.opfFormat) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:format');
elem.appendChild(OPFXML.createTextNode(config.opfFormat));
metadata.appendChild(elem);
}
// <%= publisher %>
if (typeof config.opfPublisher !== 'undefined'
&& config.opfPublisher) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:publisher');
elem.appendChild(OPFXML.createTextNode(config.opfPublisher));
metadata.appendChild(elem);
}
// <%= relation %>
if (typeof config.opfRelation !== 'undefined'
&& config.opfRelation) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:relation');
elem.appendChild(OPFXML.createTextNode(config.opfRelation));
metadata.appendChild(elem);
}
// <%= coverage %>
if (typeof config.opfCoverage !== 'undefined'
&& config.opfCoverage) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:coverage');
elem.appendChild(OPFXML.createTextNode(config.opfCoverage));
metadata.appendChild(elem);
}
// <%= rights %>
if (typeof config.opfRights !== 'undefined'
&& config.opfRights) {
elem = OPFXML.createElementNS(
'http://purl.org/dc/elements/1.1/',
'dc:rights');
elem.appendChild(OPFXML.createTextNode(config.opfRights));
metadata.appendChild(elem);
}
// - properties="<%= item.properties %>" <% }
// %>href="<%= item.href %>" media-type="<%= item.type %>"/>
// console.log(config);
let spineitems = [];
let sawNCX = false;
for (const item of config.opfManifest) {
const fullRoot = config.renderedFullPath;
const fullOpfPath = path.dirname(path.join(fullRoot, config.bookOPF));
const fullItemPath = path.join(fullRoot, item.path);
const relativeItemPath = path.relative(fullOpfPath, fullItemPath);
// console.log(`makeOpfXml item.path ${item.path} fullItemPath ${fullItemPath} relativeItemPath ${relativeItemPath}`)
let properties = '';
const set_property = (value) => {
if (!properties) properties = "";
if (properties === "") properties = value;
else properties += ' ' + value;
}
if (item.in_spine) spineitems.push(item);
const elem = OPFXML.createElement('item');
if (config.doGenerateNCX && relativeItemPath === config.sourceBookNCXHREF) {
sawNCX = true;
elem.setAttribute('id', config.sourceBookNCXID);
} else {
elem.setAttribute('id', item.id);
}
if (item.is_nav) set_property('nav');
if (item.is_cover_image) set_property('cover-image');
if (item.is_mathml) set_property('mathml');
if (item.is_scripted) set_property('scripted');
if (item.is_svg) set_property('svg');
if (item.is_remote_resources) set_property('remote-resources');
if (item.is_switch) set_property('switch');
if (properties !== '') elem.setAttribute('properties', properties);
elem.setAttribute('href', relativeItemPath);
if (config.doGenerateNCX && relativeItemPath === config.sourceBookNCXHREF) {
elem.setAttribute('media-type', 'application/x-dtbncx+xml');
} else {
elem.setAttribute('media-type', item.mime);
}
manifestElem.appendChild(elem);
}
// Patch in the NCX if it's supposed to be there
if (config.doGenerateNCX && !sawNCX) {
const elem = OPFXML.createElement('item');
elem.setAttribute('id', config.sourceBookNCXID);
const fullRoot = config.renderedFullPath;
const fullOpfPath = path.dirname(path.join(fullRoot, config.bookOPF));
const fullItemPath = path.join(fullRoot, config.sourceBookNCXHREF);
const relativeItemPath = path.relative(fullOpfPath, fullItemPath);
elem.setAttribute('href', relativeItemPath);
elem.setAttribute('media-type', 'application/x-dtbncx+xml');
manifestElem.appendChild(elem);
}
// If we're supposed to generate an NCX, at this point what we do is
// add NCX entries to the OPF
if (config.doGenerateNCX) {
/* let elem = OPFXML.createElement('item');
elem.setAttribute('id', config.sourceBookNCXID);
elem.setAttribute('href', config.sourceBookNCXHREF);
elem.setAttribute('media-type', 'application/x-dtbncx+xml');
manifestElem.appendChild(elem); */
spine.setAttribute('toc', config.sourceBookNCXID);
}
// linear="<%= item.linear %>" <% }
// %> /> <%
spineitems = spineitems.sort((a, b) => {
if (a.spine_order < b.spine_order) return -1;
else if (a.spine_order === b.spine_order) return 0;
else return 1;
});
for (const itemref of spineitems) {
// console.log(`spine item ${util.inspect(itemref)}`);
const elem = OPFXML.createElement('itemref');
elem.setAttribute('idref', itemref.id);
if (itemref.linear) { elem.setAttribute('linear', itemref.linear); }
spine.appendChild(elem);
}
return OPFXML;
}
export async function readTocNCX(tocncxFN) {
const tocncxTXT = await fsp.readFile(tocncxFN, 'utf8');
const NCXXML = new xmldom.DOMParser().parseFromString(tocncxTXT);
return NCXXML;
}
export async function makeNCXXML(config: Configuration) {
const NCXXML = new xmldom.DOMParser().parseFromString(' \
\
\
\
\
\
', 'text/xml');
let headElem;
let docTitleElem;
let docTitleText;
let docAuthorElem;
let docAuthorText;
let navMapElem;
let elem;
const heads = NCXXML.getElementsByTagName("head");
// util.log(util.inspect(rootfile));
for (const elem of utils.nodeListIterator(heads)) {
if (elem.nodeName.toUpperCase() === 'head'.toUpperCase()) headElem = elem;
}
const docTitles = NCXXML.getElementsByTagName("docTitle");
// util.log(util.inspect(rootfile));
for (const elem of utils.nodeListIterator(docTitles)) {
if (elem.nodeName.toUpperCase() === 'docTitle'.toUpperCase()) docTitleElem = elem;
}
const docTitleTexts = docTitleElem.getElementsByTagName("text");
for (const elem of utils.nodeListIterator(docTitleTexts)) {
if (elem.nodeName.toUpperCase() === 'text'.toUpperCase()) docTitleText = elem;
}
const docAuthors = NCXXML.getElementsByTagName("docAuthor");
// util.log(util.inspect(rootfile));
for (const elem of utils.nodeListIterator(docAuthors)) {
if (elem.nodeName.toUpperCase() === 'docAuthor'.toUpperCase()) docAuthorElem = elem;
}
const docAuthorTexts = docAuthorElem.getElementsByTagName("text");
for (const elem of utils.nodeListIterator(docAuthorTexts)) {
if (elem.nodeName.toUpperCase() === 'text'.toUpperCase()) docAuthorText = elem;
}
const navMaps = NCXXML.getElementsByTagName("navMap");
// util.log(util.inspect(rootfile));
for (const elem of utils.nodeListIterator(navMaps)) {
if (elem.nodeName.toUpperCase() === 'navMap'.toUpperCase()) navMapElem = elem;
}
let uniqueID;
config.opfIdentifiers.forEach(function(identifier) {
if (typeof identifier.unique !== 'undefined' && identifier.unique !== null) uniqueID = identifier;
});
if (!uniqueID) throw new Error("No Identifier");
//
//
if (uniqueID.ncxidentifier) {
elem = NCXXML.createElement('meta');
elem.setAttribute('name', "dtb:uid");
elem.setAttribute('content', uniqueID.ncxidentifier);
headElem.appendChild(elem);
} else {
elem = NCXXML.createElement('meta');
elem.setAttribute('name', "dtb:uid");
if (typeof uniqueID.type && uniqueID.type === "urn") {
elem.setAttribute('content', uniqueID.string);
} else if (typeof uniqueID.type && uniqueID.type === "isbn") {
elem.setAttribute('content', `urn:isbn:${uniqueID.string}`);
} else if (typeof uniqueID.type && uniqueID.type === "uuid") {
elem.setAttribute('content', `urn:uuid:${uniqueID.string}`);
} else {
throw new Error(`identifier with no type ${util.inspect(uniqueID)}`);
}
headElem.appendChild(elem);
}
//
elem = NCXXML.createElement('meta');
elem.setAttribute('name', "dtb:depth");
elem.setAttribute('content', "1");
headElem.appendChild(elem);
//
elem = NCXXML.createElement('meta');
elem.setAttribute('name', "dtb:totalPageCount");
elem.setAttribute('content', "0");
headElem.appendChild(elem);
//
elem = NCXXML.createElement('meta');
elem.setAttribute('name', "dtb:maxPageNumber");
elem.setAttribute('content', "0");
headElem.appendChild(elem);
let sawTitle = false;
for (const title of config.opfTitles) {
if (title.type === "main") {
docTitleText.appendChild(NCXXML.createTextNode(title.title));
sawTitle = true;
}
}
if (!sawTitle) {
throw new Error(`makeNCXXML saw no "main" title in ${util.inspect(config.opfTitles)}`);
}
if (config.opfCreators.length <= 0) {
throw new Error(`makeNCXXML no creators in ${util.inspect(config.opfCreators)}`);
}
if (config.opfCreators[0].name || config.opfCreators[0].nameReversed) {
docAuthorText.appendChild(NCXXML.createTextNode(
config.opfCreators[0].nameReversed
? config.opfCreators[0].nameReversed
: config.opfCreators[0].name));
}
const tocdata = await manifest_tocData(config);
// console.log(tocdata);
let spineorder = 0;
const navPointForChapter = function(chapter) {
const navPoint = NCXXML.createElement('navPoint');
navPoint.setAttribute('class', 'book');
navPoint.setAttribute('id', chapter.id);
navPoint.setAttribute('playOrder', spineorder.toString());
spineorder++;
const navLabel = NCXXML.createElement('navLabel');
const navLabelText = NCXXML.createElement('text');
navLabelText.appendChild(NCXXML.createTextNode(chapter.text));
navLabel.appendChild(navLabelText);
navPoint.appendChild(navLabel);
const content = NCXXML.createElement('content');
content.setAttribute('src', chapter.href);
navPoint.appendChild(content);
return navPoint;
};
const handleNavChapters = function(appendTo, chapters) {
chapters.forEach(chapter => {
const navPoint = navPointForChapter(chapter);
appendTo.appendChild(navPoint);
if (chapter.hasOwnProperty("children") && chapter.children) {
handleNavChapters(navPoint, chapter.children);
}
});
};
handleNavChapters(navMapElem, tocdata);
return NCXXML;
}