| 
									
										
										
										
											2021-01-22 17:41:11 +00:00
										 |  |  | import InteropService_Exporter_Base from './InteropService_Exporter_Base'; | 
					
						
							|  |  |  | import BaseModel from '../../BaseModel'; | 
					
						
							|  |  |  | import shim from '../../shim'; | 
					
						
							|  |  |  | import markupLanguageUtils from '../../markupLanguageUtils'; | 
					
						
							|  |  |  | import Folder from '../../models/Folder'; | 
					
						
							|  |  |  | import Note from '../../models/Note'; | 
					
						
							|  |  |  | import Setting from '../../models/Setting'; | 
					
						
							|  |  |  | import { MarkupToHtml } from '@joplin/renderer'; | 
					
						
							|  |  |  | import { ResourceEntity } from '../database/types'; | 
					
						
							| 
									
										
										
										
											2020-11-05 16:58:23 +00:00
										 |  |  | const { basename, friendlySafeFilename, rtrimSlashes } = require('../../path-utils'); | 
					
						
							|  |  |  | const { themeStyle } = require('../../theme'); | 
					
						
							|  |  |  | const { dirname } = require('../../path-utils'); | 
					
						
							|  |  |  | const { escapeHtml } = require('../../string-utils.js'); | 
					
						
							| 
									
										
										
										
											2020-11-07 15:59:37 +00:00
										 |  |  | const { assetsToHeaders } = require('@joplin/renderer'); | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 18:35:46 +01:00
										 |  |  | export default class InteropService_Exporter_Html extends InteropService_Exporter_Base { | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-22 17:41:11 +00:00
										 |  |  | 	private customCss_: string; | 
					
						
							|  |  |  | 	private destDir_: string; | 
					
						
							|  |  |  | 	private filePath_: string; | 
					
						
							|  |  |  | 	private createdDirs_: string[] = []; | 
					
						
							|  |  |  | 	private resourceDir_: string; | 
					
						
							|  |  |  | 	private markupToHtml_: MarkupToHtml; | 
					
						
							|  |  |  | 	private resources_: ResourceEntity[] = []; | 
					
						
							|  |  |  | 	private style_: any; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-12 19:13:28 +00:00
										 |  |  | 	async init(path: string, options: any = {}) { | 
					
						
							| 
									
										
										
										
											2020-01-24 21:46:48 +00:00
										 |  |  | 		this.customCss_ = options.customCss ? options.customCss : ''; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 		if (this.metadata().target === 'file') { | 
					
						
							|  |  |  | 			this.destDir_ = dirname(path); | 
					
						
							|  |  |  | 			this.filePath_ = path; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this.destDir_ = path; | 
					
						
							|  |  |  | 			this.filePath_ = null; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.resourceDir_ = this.destDir_ ? `${this.destDir_}/_resources` : null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		await shim.fsDriver().mkdir(this.destDir_); | 
					
						
							| 
									
										
										
										
											2020-12-19 17:42:18 +00:00
										 |  |  | 		this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml(options.plugins); | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 		this.style_ = themeStyle(Setting.THEME_LIGHT); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-12 19:13:28 +00:00
										 |  |  | 	async makeDirPath_(item: any, pathPart: string = null) { | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 		let output = ''; | 
					
						
							|  |  |  | 		while (true) { | 
					
						
							|  |  |  | 			if (item.type_ === BaseModel.TYPE_FOLDER) { | 
					
						
							|  |  |  | 				if (pathPart) { | 
					
						
							|  |  |  | 					output = `${pathPart}/${output}`; | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					output = `${friendlySafeFilename(item.title, null, true)}/${output}`; | 
					
						
							|  |  |  | 					output = await shim.fsDriver().findUniqueFilename(output); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (!item.parent_id) return output; | 
					
						
							|  |  |  | 			item = await Folder.load(item.parent_id); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-12 19:13:28 +00:00
										 |  |  | 	async processNoteResources_(item: any) { | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 		const target = this.metadata().target; | 
					
						
							|  |  |  | 		const linkedResourceIds = await Note.linkedResourceIds(item.body); | 
					
						
							|  |  |  | 		const relativePath = target === 'directory' ? rtrimSlashes(await this.makeDirPath_(item, '..')) : ''; | 
					
						
							|  |  |  | 		const resourcePaths = this.context() && this.context().resourcePaths ? this.context().resourcePaths : {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		let newBody = item.body; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (let i = 0; i < linkedResourceIds.length; i++) { | 
					
						
							|  |  |  | 			const id = linkedResourceIds[i]; | 
					
						
							| 
									
										
										
										
											2019-12-30 21:54:13 +01:00
										 |  |  | 			const resourceContent = `${relativePath ? `${relativePath}/` : ''}_resources/${basename(resourcePaths[id])}`; | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 			newBody = newBody.replace(new RegExp(`:/${id}`, 'g'), resourceContent); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return newBody; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-12 19:13:28 +00:00
										 |  |  | 	async processItem(_itemType: number, item: any) { | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 		if ([BaseModel.TYPE_NOTE, BaseModel.TYPE_FOLDER].indexOf(item.type_) < 0) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		let dirPath = ''; | 
					
						
							|  |  |  | 		if (!this.filePath_) { | 
					
						
							|  |  |  | 			dirPath = `${this.destDir_}/${await this.makeDirPath_(item)}`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (this.createdDirs_.indexOf(dirPath) < 0) { | 
					
						
							|  |  |  | 				await shim.fsDriver().mkdir(dirPath); | 
					
						
							|  |  |  | 				this.createdDirs_.push(dirPath); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (item.type_ === BaseModel.TYPE_NOTE) { | 
					
						
							|  |  |  | 			let noteFilePath = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (this.filePath_) { | 
					
						
							|  |  |  | 				noteFilePath = this.filePath_; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				noteFilePath = `${dirPath}/${friendlySafeFilename(item.title, null, true)}.html`; | 
					
						
							|  |  |  | 				noteFilePath = await shim.fsDriver().findUniqueFilename(noteFilePath); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const bodyMd = await this.processNoteResources_(item); | 
					
						
							| 
									
										
										
										
											2020-01-24 21:46:48 +00:00
										 |  |  | 			const result = await this.markupToHtml_.render(item.markup_language, bodyMd, this.style_, { | 
					
						
							|  |  |  | 				resources: this.resources_, | 
					
						
							|  |  |  | 				plainResourceRendering: true, | 
					
						
							|  |  |  | 				userCss: this.customCss_, | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 			const noteContent = []; | 
					
						
							| 
									
										
										
										
											2019-12-17 09:44:48 +00:00
										 |  |  | 			if (item.title) noteContent.push(`<div class="exported-note-title">${escapeHtml(item.title)}</div>`); | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 			if (result.html) noteContent.push(result.html); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-08 02:20:56 +00:00
										 |  |  | 			const libRootPath = dirname(dirname(__dirname)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-30 20:44:15 +01:00
										 |  |  | 			// We need to export all the plugin assets too and refer them from the header
 | 
					
						
							|  |  |  | 			// The source path is a bit hard-coded but shouldn't change.
 | 
					
						
							|  |  |  | 			for (let i = 0; i < result.pluginAssets.length; i++) { | 
					
						
							|  |  |  | 				const asset = result.pluginAssets[i]; | 
					
						
							| 
									
										
										
										
											2020-11-08 02:20:56 +00:00
										 |  |  | 				const filePath = `${libRootPath}/node_modules/@joplin/renderer/assets/${asset.name}`; | 
					
						
							| 
									
										
										
										
											2019-12-30 20:44:15 +01:00
										 |  |  | 				const destPath = `${dirname(noteFilePath)}/pluginAssets/${asset.name}`; | 
					
						
							|  |  |  | 				await shim.fsDriver().mkdir(dirname(destPath)); | 
					
						
							|  |  |  | 				await shim.fsDriver().copy(filePath, destPath); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-17 09:44:48 +00:00
										 |  |  | 			const fullHtml = `
 | 
					
						
							|  |  |  | 				<!DOCTYPE html> | 
					
						
							|  |  |  | 				<html> | 
					
						
							|  |  |  | 					<head> | 
					
						
							|  |  |  | 						<meta charset="UTF-8"> | 
					
						
							| 
									
										
										
										
											2020-02-08 11:11:04 +00:00
										 |  |  | 						${assetsToHeaders(result.pluginAssets, { asHtml: true })} | 
					
						
							| 
									
										
										
										
											2019-12-17 09:44:48 +00:00
										 |  |  | 						<title>${escapeHtml(item.title)}</title> | 
					
						
							|  |  |  | 					</head> | 
					
						
							|  |  |  | 					<body> | 
					
						
							|  |  |  | 						<div class="exported-note">${noteContent.join('\n\n')}</div> | 
					
						
							|  |  |  | 					</body> | 
					
						
							|  |  |  | 				</html> | 
					
						
							|  |  |  | 			`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await shim.fsDriver().writeFile(noteFilePath, fullHtml, 'utf-8'); | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-12 19:13:28 +00:00
										 |  |  | 	async processResource(resource: any, filePath: string) { | 
					
						
							| 
									
										
										
										
											2019-12-30 21:54:13 +01:00
										 |  |  | 		const destResourcePath = `${this.resourceDir_}/${basename(filePath)}`; | 
					
						
							|  |  |  | 		await shim.fsDriver().copy(filePath, destResourcePath); | 
					
						
							|  |  |  | 		this.resources_.push(resource); | 
					
						
							| 
									
										
										
										
											2019-12-15 18:41:13 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	async close() {} | 
					
						
							|  |  |  | } |