You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-06-15 23:00:36 +02:00
Desktop: Fix import interop service (#1887)
* Revert "Revert "Desktop: Add ENEX to HTML export (#1795)"" This reverts commit50b66cceca
. * Revert "Revert "Desktop, Cli: Fixed interop service so that it still allow auto-detecting importer based on format (required for Cli and for test units)"" This reverts commitc7c57ab2a5
. * Fix the .md importer * Add comment re future refactor * Rm importerClass for .md importer * Fix EnexToMd module name
This commit is contained in:
committed by
Laurent Cozic
parent
8a8ecaade3
commit
172d925f0f
170
ReactNativeClient/lib/import-enex-html-gen.js
Normal file
170
ReactNativeClient/lib/import-enex-html-gen.js
Normal file
@ -0,0 +1,170 @@
|
||||
const stringToStream = require('string-to-stream');
|
||||
const cleanHtml = require('clean-html');
|
||||
const resourceUtils = require('lib/resourceUtils.js');
|
||||
|
||||
function addResourceTag(lines, resource, attributes) {
|
||||
// Note: refactor to use Resource.markdownTag
|
||||
if (!attributes.alt) attributes.alt = resource.title;
|
||||
if (!attributes.alt) attributes.alt = resource.filename;
|
||||
if (!attributes.alt) attributes.alt = '';
|
||||
|
||||
const src = `:/${resource.id}`;
|
||||
|
||||
if (resourceUtils.isImageMimeType(resource.mime)) {
|
||||
lines.push(resourceUtils.imgElement({src, attributes}));
|
||||
} else if (resource.mime === 'audio/x-m4a') {
|
||||
/**
|
||||
* TODO: once https://github.com/laurent22/joplin/issues/1794 is resolved,
|
||||
* come back to this and make sure it works.
|
||||
*/
|
||||
lines.push(resourceUtils.audioElement({
|
||||
src,
|
||||
alt: attributes.alt,
|
||||
id: resource.id,
|
||||
}));
|
||||
} else {
|
||||
// TODO: figure out what other mime types can be handled more gracefully
|
||||
lines.push(resourceUtils.attachmentElement({
|
||||
src,
|
||||
attributes,
|
||||
id: resource.id,
|
||||
}));
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
function attributeToLowerCase(node) {
|
||||
if (!node.attributes) return {};
|
||||
let output = {};
|
||||
for (let n in node.attributes) {
|
||||
if (!node.attributes.hasOwnProperty(n)) continue;
|
||||
output[n.toLowerCase()] = node.attributes[n];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function enexXmlToHtml_(stream, resources) {
|
||||
let remainingResources = resources.slice();
|
||||
|
||||
const removeRemainingResource = id => {
|
||||
for (let i = 0; i < remainingResources.length; i++) {
|
||||
const r = remainingResources[i];
|
||||
if (r.id === id) {
|
||||
remainingResources.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {};
|
||||
const strict = false;
|
||||
var saxStream = require('sax').createStream(strict, options);
|
||||
|
||||
let section = {
|
||||
type: 'text',
|
||||
lines: [],
|
||||
parent: null,
|
||||
};
|
||||
|
||||
saxStream.on('error', function(e) {
|
||||
console.warn(e);
|
||||
// reject(e);
|
||||
});
|
||||
|
||||
|
||||
saxStream.on('text', function(text) {
|
||||
section.lines.push(text);
|
||||
});
|
||||
|
||||
saxStream.on('opentag', function(node) {
|
||||
const tagName = node.name.toLowerCase();
|
||||
const attributesStr = resourceUtils.attributesToStr(node.attributes);
|
||||
|
||||
if (tagName === 'en-media') {
|
||||
const nodeAttributes = attributeToLowerCase(node);
|
||||
const hash = nodeAttributes.hash;
|
||||
|
||||
let resource = null;
|
||||
for (let i = 0; i < resources.length; i++) {
|
||||
let r = resources[i];
|
||||
if (r.id == hash) {
|
||||
resource = r;
|
||||
removeRemainingResource(r.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
// TODO: Extract this duplicate of code in ./import-enex-md-gen.js
|
||||
let found = false;
|
||||
for (let i = 0; i < remainingResources.length; i++) {
|
||||
let r = remainingResources[i];
|
||||
if (!r.id) {
|
||||
resource = Object.assign({}, r);
|
||||
resource.id = hash;
|
||||
remainingResources.splice(i, 1);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
reject(`Hash with no associated resource: ${hash}`);
|
||||
}
|
||||
}
|
||||
|
||||
// If the resource does not appear among the note's resources, it
|
||||
// means it's an attachement. It will be appended along with the
|
||||
// other remaining resources at the bottom of the markdown text.
|
||||
if (resource && !!resource.id) {
|
||||
section.lines = addResourceTag(section.lines, resource, nodeAttributes);
|
||||
}
|
||||
} else if (tagName == 'en-todo') {
|
||||
section.lines.push('<input type="checkbox" onclick="return false;" />');
|
||||
} else if (node.isSelfClosing) {
|
||||
section.lines.push(`<${tagName}${attributesStr}>`);
|
||||
} else {
|
||||
section.lines.push(`<${tagName}${attributesStr} />`);
|
||||
}
|
||||
});
|
||||
|
||||
saxStream.on('closetag', function(n) {
|
||||
const tagName = n ? n.toLowerCase() : n;
|
||||
section.lines.push(`</${tagName}>`);
|
||||
});
|
||||
|
||||
saxStream.on('attribute', function() {});
|
||||
|
||||
saxStream.on('end', function() {
|
||||
resolve({
|
||||
content: section,
|
||||
resources: remainingResources,
|
||||
});
|
||||
});
|
||||
|
||||
stream.pipe(saxStream);
|
||||
});
|
||||
}
|
||||
|
||||
async function enexXmlToHtml(xmlString, resources, options = {}) {
|
||||
const stream = stringToStream(xmlString);
|
||||
let result = await enexXmlToHtml_(stream, resources, options);
|
||||
|
||||
try {
|
||||
const preCleaning = result.content.lines.join(''); // xmlString
|
||||
const final = await beautifyHtml(preCleaning);
|
||||
return final.join('');
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
}
|
||||
|
||||
const beautifyHtml = (html) => {
|
||||
return new Promise((resolve) => {
|
||||
const options = {wrap: 0};
|
||||
cleanHtml.clean(html, options, (...cleanedHtml) => resolve(cleanedHtml));
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {enexXmlToHtml};
|
Reference in New Issue
Block a user