mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 18:05:55 +01:00 
			
		
		
		
	getting rid of note complement WIP
This commit is contained in:
		| @@ -137,6 +137,15 @@ class Becca { | ||||
|             .map(row => new BAttachment(row)); | ||||
|     } | ||||
|  | ||||
|     /** @returns {BBlob|null} */ | ||||
|     getBlob(blobId) { | ||||
|         const row = sql.getRow("SELECT *, LENGTH(content) AS contentLength " + | ||||
|                                      "FROM blob WHERE blobId = ?", [blobId]); | ||||
|  | ||||
|         const BBlob = require("./entities/bblob"); // avoiding circular dependency problems | ||||
|         return row ? new BBlob(row) : null; | ||||
|     } | ||||
|  | ||||
|     /** @returns {BOption|null} */ | ||||
|     getOption(name) { | ||||
|         return this.options[name]; | ||||
| @@ -161,6 +170,8 @@ class Becca { | ||||
|             return this.getNoteRevision(entityId); | ||||
|         } else if (entityName === 'attachments') { | ||||
|             return this.getAttachment(entityId); | ||||
|         } else if (entityName === 'blobs') { | ||||
|             return this.getBlob(entityId); | ||||
|         } | ||||
|  | ||||
|         const camelCaseEntityName = entityName.toLowerCase().replace(/(_[a-z])/g, | ||||
|   | ||||
| @@ -142,7 +142,7 @@ class AbstractBeccaEntity { | ||||
|             throw new Error(`Cannot set null content to ${this.constructor.primaryKeyName} '${this[this.constructor.primaryKeyName]}'`); | ||||
|         } | ||||
|  | ||||
|         if (this.isStringNote()) { | ||||
|         if (this.hasStringContent()) { | ||||
|             content = content.toString(); | ||||
|         } | ||||
|         else { | ||||
| @@ -246,7 +246,7 @@ class AbstractBeccaEntity { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (this.isStringNote()) { | ||||
|         if (this.hasStringContent()) { | ||||
|             return content === null | ||||
|                 ? "" | ||||
|                 : content.toString("UTF-8"); | ||||
|   | ||||
| @@ -76,7 +76,7 @@ class BAttachment extends AbstractBeccaEntity { | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if the note has string content (not binary) */ | ||||
|     isStringNote() { | ||||
|     hasStringContent() { | ||||
|         return utils.isStringNote(this.type, this.mime); | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										26
									
								
								src/becca/entities/bblob.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/becca/entities/bblob.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| class BBlob { | ||||
|     constructor(row) { | ||||
|         /** @type {string} */ | ||||
|         this.blobId = row.blobId; | ||||
|         /** @type {string|Buffer} */ | ||||
|         this.content = row.content; | ||||
|         /** @type {number} */ | ||||
|         this.contentLength = row.contentLength; | ||||
|         /** @type {string} */ | ||||
|         this.dateModified = row.dateModified; | ||||
|         /** @type {string} */ | ||||
|         this.utcDateModified = row.utcDateModified; | ||||
|     } | ||||
|  | ||||
|     getPojo() { | ||||
|         return { | ||||
|             blobId: this.blobId, | ||||
|             content: this.content, | ||||
|             contentLength: this.contentLength, | ||||
|             dateModified: this.dateModified, | ||||
|             utcDateModified: this.utcDateModified | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = BBlob; | ||||
| @@ -103,6 +103,7 @@ class BBranch extends AbstractBeccaEntity { | ||||
|         return this.becca.notes[this.noteId]; | ||||
|     } | ||||
|  | ||||
|     /** @returns {BNote} */ | ||||
|     getNote() { | ||||
|         return this.childNote; | ||||
|     } | ||||
|   | ||||
| @@ -301,8 +301,13 @@ class BNote extends AbstractBeccaEntity { | ||||
|             || (this.type === 'file' && this.mime?.startsWith('image/')); | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if the note has string content (not binary) */ | ||||
|     /** @deprecated use hasStringContent() instead */ | ||||
|     isStringNote() { | ||||
|         return this.hasStringContent(); | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if the note has string content (not binary) */ | ||||
|     hasStringContent() { | ||||
|         return utils.isStringNote(this.type, this.mime); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -61,7 +61,7 @@ class BNoteRevision extends AbstractBeccaEntity { | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if the note has string content (not binary) */ | ||||
|     isStringNote() { | ||||
|     hasStringContent() { | ||||
|         return utils.isStringNote(this.type, this.mime); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -166,15 +166,6 @@ class NoteContext extends Component { | ||||
|         return this.notePath ? this.notePath.split('/') : []; | ||||
|     } | ||||
|  | ||||
|     /** @returns {FNoteComplement} */ | ||||
|     async getNoteComplement() { | ||||
|         if (!this.noteId) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return await froca.getNoteComplement(this.noteId); | ||||
|     } | ||||
|  | ||||
|     isActive() { | ||||
|         return appContext.tabManager.activeNtxId === this.ntxId; | ||||
|     } | ||||
|   | ||||
| @@ -30,6 +30,14 @@ class FAttachment { | ||||
|     getNote() { | ||||
|         return this.froca.notes[this.parentId]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param [opts.full=false] - force retrieval of the full note | ||||
|      * @return {FBlob} | ||||
|      */ | ||||
|     async getBlob(opts = {}) { | ||||
|         return await this.froca.getBlob('attachments', this.attachmentId, opts); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default FAttachment; | ||||
|   | ||||
							
								
								
									
										17
									
								
								src/public/app/entities/fblob.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/public/app/entities/fblob.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| class FBlob { | ||||
|     constructor(row) { | ||||
|         /** @type {string} */ | ||||
|         this.blobId = row.blobId; | ||||
|  | ||||
|         /** | ||||
|          * can either contain the whole content (in e.g. string notes), only part (large text notes) or nothing at all (binary notes, images) | ||||
|          * @type {string} | ||||
|          */ | ||||
|         this.content = row.content; | ||||
|  | ||||
|         /** @type {string} */ | ||||
|         this.dateModified = row.dateModified; | ||||
|         /** @type {string} */ | ||||
|         this.utcDateModified = row.utcDateModified; | ||||
|     } | ||||
| } | ||||
| @@ -851,13 +851,17 @@ class FNote { | ||||
|         return await this.froca.getNotes(targetRelations.map(tr => tr.noteId)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return note complement which is most importantly note's content | ||||
|      * | ||||
|      * @returns {Promise<FNoteComplement>} | ||||
|      */ | ||||
|     /** @deprecated use getBlob() instead */ | ||||
|     async getNoteComplement() { | ||||
|         return await this.froca.getNoteComplement(this.noteId); | ||||
|         return this.getBlob({ full: true }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param [opts.full=false] - force retrieval of the full note | ||||
|      * @return {FBlob} | ||||
|      */ | ||||
|     async getBlob(opts = {}) { | ||||
|         return await this.froca.getBlob('notes', this.noteId, opts); | ||||
|     } | ||||
|  | ||||
|     toString() { | ||||
|   | ||||
| @@ -1,41 +0,0 @@ | ||||
| /** | ||||
|  * FIXME: probably make it a FBlob | ||||
|  * Complements the FNote with the main note content and other extra attributes | ||||
|  */ | ||||
| class FNoteComplement { | ||||
|     constructor(row) { | ||||
|         /** @type {string} */ | ||||
|         this.noteId = row.noteId; | ||||
|  | ||||
|         /** | ||||
|          * can either contain the whole content (in e.g. string notes), only part (large text notes) or nothing at all (binary notes, images) | ||||
|          * @type {string} | ||||
|          */ | ||||
|         this.content = row.content; | ||||
|  | ||||
|         /** @type {int} */ | ||||
|         this.contentLength = row.contentLength; | ||||
|  | ||||
|         /** @type {string} */ | ||||
|         this.dateCreated = row.dateCreated; | ||||
|  | ||||
|         /** @type {string} */ | ||||
|         this.dateModified = row.dateModified; | ||||
|  | ||||
|         /** @type {string} */ | ||||
|         this.utcDateCreated = row.utcDateCreated; | ||||
|  | ||||
|         /** @type {string} */ | ||||
|         this.utcDateModified = row.utcDateModified; | ||||
|  | ||||
|         // "combined" date modified give larger out of note's and blob's dateModified | ||||
|  | ||||
|         /** @type {string} */ | ||||
|         this.combinedDateModified = row.combinedDateModified; | ||||
|  | ||||
|         /** @type {string} */ | ||||
|         this.combinedUtcDateModified = row.combinedUtcDateModified; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default FNoteComplement; | ||||
| @@ -3,7 +3,7 @@ import FNote from "../entities/fnote.js"; | ||||
| import FAttribute from "../entities/fattribute.js"; | ||||
| import server from "./server.js"; | ||||
| import appContext from "../components/app_context.js"; | ||||
| import FNoteComplement from "../entities/fnote_complement.js"; | ||||
| import FBlob from "../entities/fblob.js"; | ||||
| import FAttachment from "../entities/fattachment.js"; | ||||
|  | ||||
| /** | ||||
| @@ -38,8 +38,7 @@ class Froca { | ||||
|         /** @type {Object.<string, FAttachment>} */ | ||||
|         this.attachments = {}; | ||||
|  | ||||
|         // FIXME | ||||
|         /** @type {Object.<string, Promise<FNoteComplement>>} */ | ||||
|         /** @type {Object.<string, Promise<FBlob>>} */ | ||||
|         this.blobPromises = {}; | ||||
|  | ||||
|         this.addResp(resp); | ||||
| @@ -321,25 +320,24 @@ class Froca { | ||||
|         return attachmentRow ? new FAttachment(this, attachmentRow) : null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * // FIXME | ||||
|      * @returns {Promise<FNoteComplement>} | ||||
|      */ | ||||
|     async getNoteComplement(noteId) { | ||||
|         if (!this.blobPromises[noteId]) { | ||||
|             this.blobPromises[noteId] = server.get(`notes/${noteId}`) | ||||
|                 .then(row => new FNoteComplement(row)) | ||||
|                 .catch(e => console.error(`Cannot get note complement for note '${noteId}'`)); | ||||
|     async getBlob(entityType, entityId, opts = {}) { | ||||
|         opts.full = !!opts.full; | ||||
|         const key = `${entityType}-${entityId}`; | ||||
|  | ||||
|         if (!this.blobPromises[key]) { | ||||
|             this.blobPromises[key] = server.get(`${entityType}/${entityId}/blob?full=${opts.full}`) | ||||
|                 .then(row => new FBlob(row)) | ||||
|                 .catch(e => console.error(`Cannot get blob for ${entityType} '${entityId}'`)); | ||||
|  | ||||
|             // we don't want to keep large payloads forever in memory, so we clean that up quite quickly | ||||
|             // this cache is more meant to share the data between different components within one business transaction (e.g. loading of the note into the tab context and all the components) | ||||
|             // this is also a workaround for missing invalidation after change | ||||
|             this.blobPromises[noteId].then( | ||||
|                 () => setTimeout(() => this.blobPromises[noteId] = null, 1000) | ||||
|             this.blobPromises[key].then( | ||||
|                 () => setTimeout(() => this.blobPromises[key] = null, 1000) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return await this.blobPromises[noteId]; | ||||
|         return await this.blobPromises[key]; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -90,7 +90,7 @@ export default class TocWidget extends RightPanelWidget { | ||||
|         let $toc = "", headingCount = 0; | ||||
|         // Check for type text unconditionally in case alwaysShowWidget is set | ||||
|         if (this.note.type === 'text') { | ||||
|             const { content } = await note.getNoteComplement(); | ||||
|             const { content } = await note.getBlob(); | ||||
|             ({$toc, headingCount} = await this.getToc(content)); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,13 @@ | ||||
| const becca = require("../../becca/becca"); | ||||
| const NotFoundError = require("../../errors/not_found_error"); | ||||
| const utils = require("../../services/utils"); | ||||
| const blobService = require("../../services/blob.js"); | ||||
|  | ||||
| function getAttachmentBlob(req) { | ||||
|     const full = req.query.full === 'true'; | ||||
|  | ||||
|     return blobService.getBlobPojo('attachments', req.params.attachmentId, { full }); | ||||
| } | ||||
|  | ||||
| function getAttachments(req) { | ||||
|     const includeContent = req.query.includeContent === 'true'; | ||||
| @@ -87,6 +94,7 @@ function convertAttachmentToNote(req) { | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getAttachmentBlob, | ||||
|     getAttachments, | ||||
|     getAttachment, | ||||
|     saveAttachment, | ||||
|   | ||||
| @@ -1,13 +1,19 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const beccaService = require('../../becca/becca_service'); | ||||
| const protectedSessionService = require('../../services/protected_session'); | ||||
| const noteRevisionService = require('../../services/note_revisions'); | ||||
| const utils = require('../../services/utils'); | ||||
| const sql = require('../../services/sql'); | ||||
| const cls = require('../../services/cls'); | ||||
| const path = require('path'); | ||||
| const becca = require("../../becca/becca"); | ||||
| const blobService = require("../../services/blob.js"); | ||||
|  | ||||
| function getNoteRevisionBlob(req) { | ||||
|     const full = req.query.full === 'true'; | ||||
|  | ||||
|     return blobService.getBlobPojo('note_revisions', req.params.noteRevisionId, { full }); | ||||
| } | ||||
|  | ||||
| function getNoteRevisions(req) { | ||||
|     return becca.getNoteRevisionsFromQuery(` | ||||
| @@ -20,10 +26,11 @@ function getNoteRevisions(req) { | ||||
| } | ||||
|  | ||||
| function getNoteRevision(req) { | ||||
|     // FIXME | ||||
|     const noteRevision = becca.getNoteRevision(req.params.noteRevisionId); | ||||
|  | ||||
|     if (noteRevision.type === 'file') { | ||||
|         if (noteRevision.isStringNote()) { | ||||
|         if (noteRevision.hasStringContent()) { | ||||
|             noteRevision.content = noteRevision.getContent().substr(0, 10000); | ||||
|         } | ||||
|     } | ||||
| @@ -180,6 +187,7 @@ function getNotePathData(note) { | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getNoteRevisionBlob, | ||||
|     getNoteRevisions, | ||||
|     getNoteRevision, | ||||
|     downloadNoteRevision, | ||||
|   | ||||
| @@ -10,20 +10,20 @@ const fs = require('fs'); | ||||
| const becca = require("../../becca/becca"); | ||||
| const ValidationError = require("../../errors/validation_error"); | ||||
| const NotFoundError = require("../../errors/not_found_error"); | ||||
| const blobService = require("../../services/blob"); | ||||
|  | ||||
| function getNote(req) { | ||||
|     const noteId = req.params.noteId; | ||||
|     const note = becca.getNote(noteId); | ||||
|  | ||||
|     const note = becca.getNote(req.params.noteId); | ||||
|     if (!note) { | ||||
|         throw new NotFoundError(`Note '${noteId}' has not been found.`); | ||||
|         throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`); | ||||
|     } | ||||
|  | ||||
|     const pojo = note.getPojo(); | ||||
|  | ||||
|     if (note.isStringNote()) { | ||||
|     if (note.hasStringContent()) { | ||||
|         pojo.content = note.getContent(); | ||||
|  | ||||
|         // FIXME: use blobs instead | ||||
|         if (note.type === 'file' && pojo.content.length > 10000) { | ||||
|             pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`; | ||||
|         } | ||||
| @@ -39,6 +39,12 @@ function getNote(req) { | ||||
|     return pojo; | ||||
| } | ||||
|  | ||||
| function getNoteBlob(req) { | ||||
|     const full = req.query.full === 'true'; | ||||
|  | ||||
|     return blobService.getBlobPojo('notes', req.params.noteId, { full }); | ||||
| } | ||||
|  | ||||
| function createNote(req) { | ||||
|     const params = Object.assign({}, req.body); // clone | ||||
|     params.parentNoteId = req.params.parentNoteId; | ||||
| @@ -259,6 +265,7 @@ function convertNoteToAttachment(req) { | ||||
|  | ||||
| module.exports = { | ||||
|     getNote, | ||||
|     getNoteBlob, | ||||
|     updateNoteData, | ||||
|     deleteNote, | ||||
|     undeleteNote, | ||||
|   | ||||
| @@ -112,6 +112,7 @@ function register(app) { | ||||
|     apiRoute(PST, '/api/tree/load', treeApiRoute.load); | ||||
|  | ||||
|     apiRoute(GET, '/api/notes/:noteId', notesApiRoute.getNote); | ||||
|     apiRoute(GET, '/api/notes/:noteId/blob', notesApiRoute.getNoteBlob); | ||||
|     apiRoute(PUT, '/api/notes/:noteId/data', notesApiRoute.updateNoteData); | ||||
|     apiRoute(DEL, '/api/notes/:noteId', notesApiRoute.deleteNote); | ||||
|     apiRoute(PUT, '/api/notes/:noteId/undelete', notesApiRoute.undeleteNote); | ||||
| @@ -153,6 +154,7 @@ function register(app) { | ||||
|     apiRoute(GET, '/api/attachments/:attachmentId', attachmentsApiRoute.getAttachment); | ||||
|     apiRoute(PST, '/api/attachments/:attachmentId/convert-to-note', attachmentsApiRoute.convertAttachmentToNote); | ||||
|     apiRoute(DEL, '/api/attachments/:attachmentId', attachmentsApiRoute.deleteAttachment); | ||||
|     apiRoute(GET, '/api/attachments/:attachmentId/blob', attachmentsApiRoute.getAttachmentBlob); | ||||
|     route(GET, '/api/attachments/:attachmentId/image/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnAttachedImage); | ||||
|     route(GET, '/api/attachments/:attachmentId/open', [auth.checkApiAuthOrElectron], filesRoute.openAttachment); | ||||
|     route(GET, '/api/attachments/:attachmentId/open-partial', [auth.checkApiAuthOrElectron], | ||||
| @@ -170,6 +172,7 @@ function register(app) { | ||||
|     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); | ||||
|     apiRoute(DEL, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions); | ||||
|     apiRoute(GET, '/api/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); | ||||
|     apiRoute(GET, '/api/revisions/:noteRevisionId/blob', noteRevisionsApiRoute.getNoteRevisionBlob); | ||||
|     apiRoute(DEL, '/api/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision); | ||||
|     apiRoute(PST, '/api/revisions/:noteRevisionId/restore', noteRevisionsApiRoute.restoreNoteRevision); | ||||
|     route(GET, '/api/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision); | ||||
|   | ||||
							
								
								
									
										28
									
								
								src/services/blob.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/services/blob.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| const becca = require('../becca/becca'); | ||||
| const NotFoundError = require("../errors/not_found_error"); | ||||
|  | ||||
| function getBlobPojo(entityName, entityId, opts = {}) { | ||||
|     opts.full = !!opts.full; | ||||
|  | ||||
|     const entity = becca.getEntity(entityName, entityId); | ||||
|  | ||||
|     if (!entity) { | ||||
|         throw new NotFoundError(`Entity ${entityName} '${entityId}' was not found.`); | ||||
|     } | ||||
|  | ||||
|     const blob = becca.getBlob(entity.blobId); | ||||
|  | ||||
|     const pojo = blob.getPojo(); | ||||
|  | ||||
|     if (!entity.hasStringContent()) { | ||||
|         pojo.content = null; | ||||
|     } else if (!opts.full && pojo.content.length > 10000) { | ||||
|         pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`; | ||||
|     } | ||||
|  | ||||
|     return pojo; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getBlobPojo | ||||
| }; | ||||
| @@ -24,13 +24,13 @@ function exportToOpml(taskContext, branch, version, res) { | ||||
|  | ||||
|         if (opmlVersion === 1) { | ||||
|             const preparedTitle = escapeXmlAttribute(title); | ||||
|             const preparedContent = note.isStringNote() ? prepareText(note.getContent()) : ''; | ||||
|             const preparedContent = note.hasStringContent() ? prepareText(note.getContent()) : ''; | ||||
|  | ||||
|             res.write(`<outline title="${preparedTitle}" text="${preparedContent}">\n`); | ||||
|         } | ||||
|         else if (opmlVersion === 2) { | ||||
|             const preparedTitle = escapeXmlAttribute(title); | ||||
|             const preparedContent = note.isStringNote() ? escapeXmlAttribute(note.getContent()) : ''; | ||||
|             const preparedContent = note.hasStringContent() ? escapeXmlAttribute(note.getContent()) : ''; | ||||
|  | ||||
|             res.write(`<outline text="${preparedTitle}" _note="${preparedContent}">\n`); | ||||
|         } | ||||
|   | ||||
| @@ -90,7 +90,7 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) => | ||||
|             if (["text", "code"].includes(note.type) | ||||
|                 // if the note has already content we're not going to overwrite it with template's one | ||||
|                 && (!content || content.trim().length === 0) | ||||
|                 && templateNote.isStringNote()) { | ||||
|                 && templateNote.hasStringContent()) { | ||||
|  | ||||
|                 const templateNoteContent = templateNote.getContent(); | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,6 @@ const htmlSanitizer = require("./html_sanitizer"); | ||||
| const ValidationError = require("../errors/validation_error"); | ||||
| const noteTypesService = require("./note_types"); | ||||
| const fs = require("fs"); | ||||
| const BAttachment = require("../becca/entities/battachment"); | ||||
|  | ||||
| /** @param {BNote} parentNote */ | ||||
| function getNewNotePosition(parentNote) { | ||||
|   | ||||
| @@ -107,7 +107,7 @@ class SNote extends AbstractShacaEntity { | ||||
|  | ||||
|         let content = row.content; | ||||
|  | ||||
|         if (this.isStringNote()) { | ||||
|         if (this.hasStringContent()) { | ||||
|             return content === null | ||||
|                 ? "" | ||||
|                 : content.toString("UTF-8"); | ||||
| @@ -118,7 +118,7 @@ class SNote extends AbstractShacaEntity { | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if the note has string content (not binary) */ | ||||
|     isStringNote() { | ||||
|     hasStringContent() { | ||||
|         return utils.isStringNote(this.type, this.mime); | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user