12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="utf-8">
- <title>JSDoc: Source: becca/entities/note.js</title>
- <script src="scripts/prettify/prettify.js"> </script>
- <script src="scripts/prettify/lang-css.js"> </script>
- <!--[if lt IE 9]>
- <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
- <![endif]-->
- <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
- <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
- </head>
- <body>
- <div id="main">
- <h1 class="page-title">Source: becca/entities/note.js</h1>
-
-
- <section>
- <article>
- <pre class="prettyprint source linenums"><code>"use strict";
- const protectedSessionService = require('../../services/protected_session');
- const log = require('../../services/log');
- const sql = require('../../services/sql');
- const utils = require('../../services/utils');
- const dateUtils = require('../../services/date_utils');
- const entityChangesService = require('../../services/entity_changes');
- const AbstractEntity = require("./abstract_entity");
- const NoteRevision = require("./note_revision");
- const LABEL = 'label';
- const RELATION = 'relation';
- /**
- * Trilium's main entity which can represent text note, image, code note, file attachment etc.
- */
- class Note extends AbstractEntity {
- static get entityName() { return "notes"; }
- static get primaryKeyName() { return "noteId"; }
- static get hashedProperties() { return ["noteId", "title", "isProtected", "type", "mime"]; }
- constructor(row) {
- super();
- if (!row) {
- return;
- }
- this.updateFromRow(row);
- this.init();
- }
- updateFromRow(row) {
- this.update([
- row.noteId,
- row.title,
- row.type,
- row.mime,
- row.isProtected,
- row.dateCreated,
- row.dateModified,
- row.utcDateCreated,
- row.utcDateModified
- ]);
- }
- update([noteId, title, type, mime, isProtected, dateCreated, dateModified, utcDateCreated, utcDateModified]) {
- // ------ Database persisted attributes ------
- /** @type {string} */
- this.noteId = noteId;
- /** @type {string} */
- this.title = title;
- /** @type {boolean} */
- this.isProtected = !!isProtected;
- /** @type {string} */
- this.type = type;
- /** @type {string} */
- this.mime = mime;
- /** @type {string} */
- this.dateCreated = dateCreated || dateUtils.localNowDateTime();
- /** @type {string} */
- this.dateModified = dateModified;
- /** @type {string} */
- this.utcDateCreated = utcDateCreated || dateUtils.utcNowDateTime();
- /** @type {string} */
- this.utcDateModified = utcDateModified;
- // ------ Derived attributes ------
- /** @type {boolean} */
- this.isDecrypted = !this.noteId || !this.isProtected;
- this.decrypt();
- /** @type {string|null} */
- this.flatTextCache = null;
- return this;
- }
- init() {
- /** @type {Branch[]} */
- this.parentBranches = [];
- /** @type {Note[]} */
- this.parents = [];
- /** @type {Note[]} */
- this.children = [];
- /** @type {Attribute[]} */
- this.ownedAttributes = [];
- /** @type {Attribute[]|null}
- * @private */
- this.__attributeCache = null;
- /** @type {Attribute[]|null}
- * @private*/
- this.inheritableAttributeCache = null;
- /** @type {Attribute[]} */
- this.targetRelations = [];
- this.becca.addNote(this.noteId, this);
- /** @type {Note[]|null}
- * @private */
- this.ancestorCache = null;
- // following attributes are filled during searching from database
- /**
- * size of the content in bytes
- * @type {int|null}
- */
- this.contentSize = null;
- /**
- * size of the content and note revision contents in bytes
- * @type {int|null}
- */
- this.noteSize = null;
- /**
- * number of note revisions for this note
- * @type {int|null}
- */
- this.revisionCount = null;
- }
- isContentAvailable() {
- return !this.noteId // new note which was not encrypted yet
- || !this.isProtected
- || protectedSessionService.isProtectedSessionAvailable()
- }
- getTitleOrProtected() {
- return this.isContentAvailable() ? this.title : '[protected]';
- }
- /** @returns {Branch[]} */
- getParentBranches() {
- return this.parentBranches;
- }
- /**
- * @returns {Branch[]}
- * @deprecated use getParentBranches() instead
- */
- getBranches() {
- return this.parentBranches;
- }
- /** @returns {Note[]} */
- getParentNotes() {
- return this.parents;
- }
- /** @returns {Note[]} */
- getChildNotes() {
- return this.children;
- }
- /** @returns {boolean} */
- hasChildren() {
- return this.children && this.children.length > 0;
- }
- /** @returns {Branch[]} */
- getChildBranches() {
- return this.children.map(childNote => this.becca.getBranchFromChildAndParent(childNote.noteId, this.noteId));
- }
- /*
- * Note content has quite special handling - it's not a separate entity, but a lazily loaded
- * part of Note entity with it's own sync. Reasons behind this hybrid design has been:
- *
- * - content can be quite large and it's not necessary to load it / fill memory for any note access even if we don't need a content, especially for bulk operations like search
- * - changes in the note metadata or title should not trigger note content sync (so we keep separate utcDateModified and entity changes records)
- * - but to the user note content and title changes are one and the same - single dateModified (so all changes must go through Note and content is not a separate entity)
- */
- /** @returns {*} */
- getContent(silentNotFoundError = false) {
- const row = sql.getRow(`SELECT content FROM note_contents WHERE noteId = ?`, [this.noteId]);
- if (!row) {
- if (silentNotFoundError) {
- return undefined;
- }
- else {
- throw new Error("Cannot find note content for noteId=" + this.noteId);
- }
- }
- let content = row.content;
- if (this.isProtected) {
- if (protectedSessionService.isProtectedSessionAvailable()) {
- content = content === null ? null : protectedSessionService.decrypt(content);
- }
- else {
- content = "";
- }
- }
- if (this.isStringNote()) {
- return content === null
- ? ""
- : content.toString("UTF-8");
- }
- else {
- return content;
- }
- }
- /** @returns {{contentLength, dateModified, utcDateModified}} */
- getContentMetadata() {
- return sql.getRow(`
- SELECT
- LENGTH(content) AS contentLength,
- dateModified,
- utcDateModified
- FROM note_contents
- WHERE noteId = ?`, [this.noteId]);
- }
- /** @returns {*} */
- getJsonContent() {
- const content = this.getContent();
- if (!content || !content.trim()) {
- return null;
- }
- return JSON.parse(content);
- }
- setContent(content, ignoreMissingProtectedSession = false) {
- if (content === null || content === undefined) {
- throw new Error(`Cannot set null content to note ${this.noteId}`);
- }
- if (this.isStringNote()) {
- content = content.toString();
- }
- else {
- content = Buffer.isBuffer(content) ? content : Buffer.from(content);
- }
- const pojo = {
- noteId: this.noteId,
- content: content,
- dateModified: dateUtils.localNowDateTime(),
- utcDateModified: dateUtils.utcNowDateTime()
- };
- if (this.isProtected) {
- if (protectedSessionService.isProtectedSessionAvailable()) {
- pojo.content = protectedSessionService.encrypt(pojo.content);
- }
- else if (!ignoreMissingProtectedSession) {
- throw new Error(`Cannot update content of noteId=${this.noteId} since we're out of protected session.`);
- }
- }
- sql.upsert("note_contents", "noteId", pojo);
- const hash = utils.hash(this.noteId + "|" + pojo.content.toString());
- entityChangesService.addEntityChange({
- entityName: 'note_contents',
- entityId: this.noteId,
- hash: hash,
- isErased: false,
- utcDateChanged: pojo.utcDateModified,
- isSynced: true
- });
- }
- setJsonContent(content) {
- this.setContent(JSON.stringify(content, null, '\t'));
- }
- /** @returns {boolean} true if this note is the root of the note tree. Root note has "root" noteId */
- isRoot() {
- return this.noteId === 'root';
- }
- /** @returns {boolean} true if this note is of application/json content type */
- isJson() {
- return this.mime === "application/json";
- }
- /** @returns {boolean} true if this note is JavaScript (code or attachment) */
- isJavaScript() {
- return (this.type === "code" || this.type === "file")
- && (this.mime.startsWith("application/javascript")
- || this.mime === "application/x-javascript"
- || this.mime === "text/javascript");
- }
- /** @returns {boolean} true if this note is HTML */
- isHtml() {
- return ["code", "file", "render"].includes(this.type)
- && this.mime === "text/html";
- }
- /** @returns {boolean} true if the note has string content (not binary) */
- isStringNote() {
- return utils.isStringNote(this.type, this.mime);
- }
- /** @returns {string|null} JS script environment - either "frontend" or "backend" */
- getScriptEnv() {
- if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith('env=frontend'))) {
- return "frontend";
- }
- if (this.type === 'render') {
- return "frontend";
- }
- if (this.isJavaScript() && this.mime.endsWith('env=backend')) {
- return "backend";
- }
- return null;
- }
- /**
- * @param {string} [type] - (optional) attribute type to filter
- * @param {string} [name] - (optional) attribute name to filter
- * @returns {Attribute[]} all note's attributes, including inherited ones
- */
- getAttributes(type, name) {
- this.__getAttributes([]);
- if (type && name) {
- return this.__attributeCache.filter(attr => attr.type === type && attr.name === name);
- }
- else if (type) {
- return this.__attributeCache.filter(attr => attr.type === type);
- }
- else if (name) {
- return this.__attributeCache.filter(attr => attr.name === name);
- }
- else {
- return this.__attributeCache.slice();
- }
- }
- __getAttributes(path) {
- if (path.includes(this.noteId)) {
- return [];
- }
- if (!this.__attributeCache) {
- const parentAttributes = this.ownedAttributes.slice();
- const newPath = [...path, this.noteId];
- if (this.noteId !== 'root') {
- for (const parentNote of this.parents) {
- parentAttributes.push(...parentNote.__getInheritableAttributes(newPath));
- }
- }
- const templateAttributes = [];
- for (const ownedAttr of parentAttributes) { // parentAttributes so we process also inherited templates
- if (ownedAttr.type === 'relation' && ownedAttr.name === 'template') {
- const templateNote = this.becca.notes[ownedAttr.value];
- if (templateNote) {
- templateAttributes.push(...templateNote.__getAttributes(newPath));
- }
- }
- }
- this.__attributeCache = [];
- const addedAttributeIds = new Set();
- for (const attr of parentAttributes.concat(templateAttributes)) {
- if (!addedAttributeIds.has(attr.attributeId)) {
- addedAttributeIds.add(attr.attributeId);
- this.__attributeCache.push(attr);
- }
- }
- this.inheritableAttributeCache = [];
- for (const attr of this.__attributeCache) {
- if (attr.isInheritable) {
- this.inheritableAttributeCache.push(attr);
- }
- }
- }
- return this.__attributeCache;
- }
- /** @returns {Attribute[]} */
- __getInheritableAttributes(path) {
- if (path.includes(this.noteId)) {
- return [];
- }
- if (!this.inheritableAttributeCache) {
- this.__getAttributes(path); // will refresh also this.inheritableAttributeCache
- }
- return this.inheritableAttributeCache;
- }
- hasAttribute(type, name) {
- return !!this.getAttributes().find(attr => attr.type === type && attr.name === name);
- }
- getAttributeCaseInsensitive(type, name, value) {
- name = name.toLowerCase();
- value = value ? value.toLowerCase() : null;
- return this.getAttributes().find(
- attr => attr.type === type
- && attr.name.toLowerCase() === name
- && (!value || attr.value.toLowerCase() === value));
- }
- getRelationTarget(name) {
- const relation = this.getAttributes().find(attr => attr.type === 'relation' && attr.name === name);
- return relation ? relation.targetNote : null;
- }
- /**
- * @param {string} name - label name
- * @returns {boolean} true if label exists (including inherited)
- */
- hasLabel(name) { return this.hasAttribute(LABEL, name); }
- /**
- * @param {string} name - label name
- * @returns {boolean} true if label exists (excluding inherited)
- */
- hasOwnedLabel(name) { return this.hasOwnedAttribute(LABEL, name); }
- /**
- * @param {string} name - relation name
- * @returns {boolean} true if relation exists (including inherited)
- */
- hasRelation(name) { return this.hasAttribute(RELATION, name); }
- /**
- * @param {string} name - relation name
- * @returns {boolean} true if relation exists (excluding inherited)
- */
- hasOwnedRelation(name) { return this.hasOwnedAttribute(RELATION, name); }
- /**
- * @param {string} name - label name
- * @returns {Attribute|null} label if it exists, null otherwise
- */
- getLabel(name) { return this.getAttribute(LABEL, name); }
- /**
- * @param {string} name - label name
- * @returns {Attribute|null} label if it exists, null otherwise
- */
- getOwnedLabel(name) { return this.getOwnedAttribute(LABEL, name); }
- /**
- * @param {string} name - relation name
- * @returns {Attribute|null} relation if it exists, null otherwise
- */
- getRelation(name) { return this.getAttribute(RELATION, name); }
- /**
- * @param {string} name - relation name
- * @returns {Attribute|null} relation if it exists, null otherwise
- */
- getOwnedRelation(name) { return this.getOwnedAttribute(RELATION, name); }
- /**
- * @param {string} name - label name
- * @returns {string|null} label value if label exists, null otherwise
- */
- getLabelValue(name) { return this.getAttributeValue(LABEL, name); }
- /**
- * @param {string} name - label name
- * @returns {string|null} label value if label exists, null otherwise
- */
- getOwnedLabelValue(name) { return this.getOwnedAttributeValue(LABEL, name); }
- /**
- * @param {string} name - relation name
- * @returns {string|null} relation value if relation exists, null otherwise
- */
- getRelationValue(name) { return this.getAttributeValue(RELATION, name); }
- /**
- * @param {string} name - relation name
- * @returns {string|null} relation value if relation exists, null otherwise
- */
- getOwnedRelationValue(name) { return this.getOwnedAttributeValue(RELATION, name); }
- /**
- * @param {string} type - attribute type (label, relation, etc.)
- * @param {string} name - attribute name
- * @returns {boolean} true if note has an attribute with given type and name (excluding inherited)
- */
- hasOwnedAttribute(type, name) {
- return !!this.getOwnedAttribute(type, name);
- }
- /**
- * @param {string} type - attribute type (label, relation, etc.)
- * @param {string} name - attribute name
- * @returns {Attribute} attribute of given type and name. If there's more such attributes, first is returned. Returns null if there's no such attribute belonging to this note.
- */
- getAttribute(type, name) {
- const attributes = this.getAttributes();
- return attributes.find(attr => attr.type === type && attr.name === name);
- }
- /**
- * @param {string} type - attribute type (label, relation, etc.)
- * @param {string} name - attribute name
- * @returns {string|null} attribute value of given type and name or null if no such attribute exists.
- */
- getAttributeValue(type, name) {
- const attr = this.getAttribute(type, name);
- return attr ? attr.value : null;
- }
- /**
- * @param {string} type - attribute type (label, relation, etc.)
- * @param {string} name - attribute name
- * @returns {string|null} attribute value of given type and name or null if no such attribute exists.
- */
- getOwnedAttributeValue(type, name) {
- const attr = this.getOwnedAttribute(type, name);
- return attr ? attr.value : null;
- }
- /**
- * @param {string} [name] - label name to filter
- * @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones
- */
- getLabels(name) {
- return this.getAttributes(LABEL, name);
- }
- /**
- * @param {string} [name] - label name to filter
- * @returns {string[]} all note's label values, including inherited ones
- */
- getLabelValues(name) {
- return this.getLabels(name).map(l => l.value);
- }
- /**
- * @param {string} [name] - label name to filter
- * @returns {Attribute[]} all note's labels (attributes with type label), excluding inherited ones
- */
- getOwnedLabels(name) {
- return this.getOwnedAttributes(LABEL, name);
- }
- /**
- * @param {string} [name] - label name to filter
- * @returns {string[]} all note's label values, excluding inherited ones
- */
- getOwnedLabelValues(name) {
- return this.getOwnedAttributes(LABEL, name).map(l => l.value);
- }
- /**
- * @param {string} [name] - relation name to filter
- * @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones
- */
- getRelations(name) {
- return this.getAttributes(RELATION, name);
- }
- /**
- * @param {string} [name] - relation name to filter
- * @returns {Attribute[]} all note's relations (attributes with type relation), excluding inherited ones
- */
- getOwnedRelations(name) {
- return this.getOwnedAttributes(RELATION, name);
- }
- /**
- * @param {string} [type] - (optional) attribute type to filter
- * @param {string} [name] - (optional) attribute name to filter
- * @returns {Attribute[]} note's "owned" attributes - excluding inherited ones
- */
- getOwnedAttributes(type, name) {
- // it's a common mistake to include # or ~ into attribute name
- if (name && ["#", "~"].includes(name[0])) {
- name = name.substr(1);
- }
- if (type && name) {
- return this.ownedAttributes.filter(attr => attr.type === type && attr.name === name);
- }
- else if (type) {
- return this.ownedAttributes.filter(attr => attr.type === type);
- }
- else if (name) {
- return this.ownedAttributes.filter(attr => attr.name === name);
- }
- else {
- return this.ownedAttributes.slice();
- }
- }
- /**
- * @returns {Attribute} attribute belonging to this specific note (excludes inherited attributes)
- *
- * This method can be significantly faster than the getAttribute()
- */
- getOwnedAttribute(type, name) {
- const attrs = this.getOwnedAttributes(type, name);
- return attrs.length > 0 ? attrs[0] : null;
- }
- get isArchived() {
- return this.hasAttribute('label', 'archived');
- }
- hasInheritableOwnedArchivedLabel() {
- return !!this.ownedAttributes.find(attr => attr.type === 'label' && attr.name === 'archived' && attr.isInheritable);
- }
- // will sort the parents so that non-search & non-archived are first and archived at the end
- // this is done so that non-search & non-archived paths are always explored as first when looking for note path
- sortParents() {
- this.parentBranches.sort((a, b) =>
- a.branchId.startsWith('virt-')
- || a.parentNote.hasInheritableOwnedArchivedLabel() ? 1 : -1);
- this.parents = this.parentBranches.map(branch => branch.parentNote);
- }
- /**
- * This is used for:
- * - fast searching
- * - note similarity evaluation
- *
- * @return {string} - returns flattened textual representation of note, prefixes and attributes
- */
- getFlatText() {
- if (!this.flatTextCache) {
- this.flatTextCache = this.noteId + ' ' + this.type + ' ' + this.mime + ' ';
- for (const branch of this.parentBranches) {
- if (branch.prefix) {
- this.flatTextCache += branch.prefix + ' ';
- }
- }
- this.flatTextCache += this.title + ' ';
- for (const attr of this.getAttributes()) {
- // it's best to use space as separator since spaces are filtered from the search string by the tokenization into words
- this.flatTextCache += (attr.type === 'label' ? '#' : '~') + attr.name;
- if (attr.value) {
- this.flatTextCache += '=' + attr.value;
- }
- this.flatTextCache += ' ';
- }
- this.flatTextCache = utils.normalize(this.flatTextCache);
- }
- return this.flatTextCache;
- }
- invalidateThisCache() {
- this.flatTextCache = null;
- this.__attributeCache = null;
- this.inheritableAttributeCache = null;
- this.ancestorCache = null;
- }
- invalidateSubTree(path = []) {
- if (path.includes(this.noteId)) {
- return;
- }
- this.invalidateThisCache();
- if (this.children.length || this.targetRelations.length) {
- path = [...path, this.noteId];
- }
- for (const childNote of this.children) {
- childNote.invalidateSubTree(path);
- }
- for (const targetRelation of this.targetRelations) {
- if (targetRelation.name === 'template') {
- const note = targetRelation.note;
- if (note) {
- note.invalidateSubTree(path);
- }
- }
- }
- }
- invalidateSubtreeFlatText() {
- this.flatTextCache = null;
- for (const childNote of this.children) {
- childNote.invalidateSubtreeFlatText();
- }
- for (const targetRelation of this.targetRelations) {
- if (targetRelation.name === 'template') {
- const note = targetRelation.note;
- if (note) {
- note.invalidateSubtreeFlatText();
- }
- }
- }
- }
- getRelationDefinitions() {
- return this.getLabels()
- .filter(l => l.name.startsWith("relation:"));
- }
- getLabelDefinitions() {
- return this.getLabels()
- .filter(l => l.name.startsWith("relation:"));
- }
- isTemplate() {
- return !!this.targetRelations.find(rel => rel.name === 'template');
- }
- /** @returns {Note[]} */
- getSubtreeNotesIncludingTemplated() {
- const set = new Set();
- function inner(note) {
- if (set.has(note)) {
- return;
- }
- set.add(note);
- for (const childNote of note.children) {
- inner(childNote);
- }
- for (const targetRelation of note.targetRelations) {
- if (targetRelation.name === 'template') {
- const targetNote = targetRelation.note;
- if (targetNote) {
- inner(targetNote);
- }
- }
- }
- }
- inner(this);
- return Array.from(set);
- }
- /** @returns {Note[]} */
- getSubtreeNotes(includeArchived = true) {
- const noteSet = new Set();
- function addSubtreeNotesInner(note) {
- if (!includeArchived && note.isArchived) {
- return;
- }
- noteSet.add(note);
- for (const childNote of note.children) {
- addSubtreeNotesInner(childNote);
- }
- }
- addSubtreeNotesInner(this);
- return Array.from(noteSet);
- }
- /** @returns {String[]} */
- getSubtreeNoteIds(includeArchived = true) {
- return this.getSubtreeNotes(includeArchived).map(note => note.noteId);
- }
- getDescendantNoteIds() {
- return this.getSubtreeNoteIds();
- }
- get parentCount() {
- return this.parents.length;
- }
- get childrenCount() {
- return this.children.length;
- }
- get labelCount() {
- return this.getAttributes().filter(attr => attr.type === 'label').length;
- }
- get ownedLabelCount() {
- return this.ownedAttributes.filter(attr => attr.type === 'label').length;
- }
- get relationCount() {
- return this.getAttributes().filter(attr => attr.type === 'relation' && !attr.isAutoLink()).length;
- }
- get relationCountIncludingLinks() {
- return this.getAttributes().filter(attr => attr.type === 'relation').length;
- }
- get ownedRelationCount() {
- return this.ownedAttributes.filter(attr => attr.type === 'relation' && !attr.isAutoLink()).length;
- }
- get ownedRelationCountIncludingLinks() {
- return this.ownedAttributes.filter(attr => attr.type === 'relation').length;
- }
- get targetRelationCount() {
- return this.targetRelations.filter(attr => !attr.isAutoLink()).length;
- }
- get targetRelationCountIncludingLinks() {
- return this.targetRelations.length;
- }
- get attributeCount() {
- return this.getAttributes().length;
- }
- get ownedAttributeCount() {
- return this.getAttributes().length;
- }
- /** @returns {Note[]} */
- getAncestors() {
- if (!this.ancestorCache) {
- const noteIds = new Set();
- this.ancestorCache = [];
- for (const parent of this.parents) {
- if (noteIds.has(parent.noteId)) {
- continue;
- }
- this.ancestorCache.push(parent);
- noteIds.add(parent.noteId);
- for (const ancestorNote of parent.getAncestors()) {
- if (!noteIds.has(ancestorNote.noteId)) {
- this.ancestorCache.push(ancestorNote);
- noteIds.add(ancestorNote.noteId);
- }
- }
- }
- }
- return this.ancestorCache;
- }
- /** @returns {boolean} */
- hasAncestor(ancestorNoteId) {
- for (const ancestorNote of this.getAncestors()) {
- if (ancestorNote.noteId === ancestorNoteId) {
- return true;
- }
- }
- return false;
- }
- getTargetRelations() {
- return this.targetRelations;
- }
- /** @returns {Note[]} - returns only notes which are templated, does not include their subtrees
- * in effect returns notes which are influenced by note's non-inheritable attributes */
- getTemplatedNotes() {
- const arr = [this];
- for (const targetRelation of this.targetRelations) {
- if (targetRelation.name === 'template') {
- const note = targetRelation.note;
- if (note) {
- arr.push(note);
- }
- }
- }
- return arr;
- }
- getDistanceToAncestor(ancestorNoteId) {
- if (this.noteId === ancestorNoteId) {
- return 0;
- }
- let minDistance = 999999;
- for (const parent of this.parents) {
- minDistance = Math.min(minDistance, parent.getDistanceToAncestor(ancestorNoteId) + 1);
- }
- return minDistance;
- }
- getNoteRevisions() {
- return sql.getRows("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId])
- .map(row => new NoteRevision(row));
- }
- /**
- * @return {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
- */
- getAllNotePaths() {
- if (this.noteId === 'root') {
- return [['root']];
- }
- const notePaths = [];
- for (const parentNote of this.getParentNotes()) {
- for (const parentPath of parentNote.getAllNotePaths()) {
- parentPath.push(this.noteId);
- notePaths.push(parentPath);
- }
- }
- return notePaths;
- }
- /**
- * @param ancestorNoteId
- * @return {boolean} - true if ancestorNoteId occurs in at least one of the note's paths
- */
- isDescendantOfNote(ancestorNoteId) {
- const notePaths = this.getAllNotePaths();
- return notePaths.some(path => path.includes(ancestorNoteId));
- }
- /**
- * Update's given attribute's value or creates it if it doesn't exist
- *
- * @param {string} type - attribute type (label, relation, etc.)
- * @param {string} name - attribute name
- * @param {string} [value] - attribute value (optional)
- */
- setAttribute(type, name, value) {
- const attributes = this.getOwnedAttributes();
- const attr = attributes.find(attr => attr.type === type && attr.name === name);
- value = value !== null && value !== undefined ? value.toString() : "";
- if (attr) {
- if (attr.value !== value) {
- attr.value = value;
- attr.save();
- }
- }
- else {
- const Attribute = require("./attribute");
- new Attribute({
- noteId: this.noteId,
- type: type,
- name: name,
- value: value
- }).save();
- }
- }
- /**
- * Removes given attribute name-value pair if it exists.
- *
- * @param {string} type - attribute type (label, relation, etc.)
- * @param {string} name - attribute name
- * @param {string} [value] - attribute value (optional)
- */
- removeAttribute(type, name, value) {
- const attributes = this.getOwnedAttributes();
- for (const attribute of attributes) {
- if (attribute.type === type && attribute.name === name && (value === undefined || value === attribute.value)) {
- attribute.markAsDeleted();
- }
- }
- }
- /**
- * @return {Attribute}
- */
- addAttribute(type, name, value = "", isInheritable = false, position = 1000) {
- const Attribute = require("./attribute");
- return new Attribute({
- noteId: this.noteId,
- type: type,
- name: name,
- value: value,
- isInheritable: isInheritable,
- position: position
- }).save();
- }
- addLabel(name, value = "", isInheritable = false) {
- return this.addAttribute(LABEL, name, value, isInheritable);
- }
- addRelation(name, targetNoteId, isInheritable = false) {
- return this.addAttribute(RELATION, name, targetNoteId, isInheritable);
- }
- /**
- * Based on enabled, attribute is either set or removed.
- *
- * @param {string} type - attribute type ('relation', 'label' etc.)
- * @param {boolean} enabled - toggle On or Off
- * @param {string} name - attribute name
- * @param {string} [value] - attribute value (optional)
- */
- toggleAttribute(type, enabled, name, value) {
- if (enabled) {
- this.setAttribute(type, name, value);
- }
- else {
- this.removeAttribute(type, name, value);
- }
- }
- /**
- * Based on enabled, label is either set or removed.
- *
- * @param {boolean} enabled - toggle On or Off
- * @param {string} name - label name
- * @param {string} [value] - label value (optional)
- */
- toggleLabel(enabled, name, value) { return this.toggleAttribute(LABEL, enabled, name, value); }
- /**
- * Based on enabled, relation is either set or removed.
- *
- * @param {boolean} enabled - toggle On or Off
- * @param {string} name - relation name
- * @param {string} [value] - relation value (noteId)
- */
- toggleRelation(enabled, name, value) { return this.toggleAttribute(RELATION, enabled, name, value); }
- /**
- * Update's given label's value or creates it if it doesn't exist
- *
- * @param {string} name - label name
- * @param {string} [value] - label value
- */
- setLabel(name, value) { return this.setAttribute(LABEL, name, value); }
- /**
- * Update's given relation's value or creates it if it doesn't exist
- *
- * @param {string} name - relation name
- * @param {string} value - relation value (noteId)
- */
- setRelation(name, value) { return this.setAttribute(RELATION, name, value); }
- /**
- * Remove label name-value pair, if it exists.
- *
- * @param {string} name - label name
- * @param {string} [value] - label value
- */
- removeLabel(name, value) { return this.removeAttribute(LABEL, name, value); }
- /**
- * Remove relation name-value pair, if it exists.
- *
- * @param {string} name - relation name
- * @param {string} [value] - relation value (noteId)
- */
- removeRelation(name, value) { return this.removeAttribute(RELATION, name, value); }
- searchNotesInSubtree(searchString) {
- const searchService = require("../../services/search/services/search");
- return searchService.searchNotes(searchString);
- }
- searchNoteInSubtree(searchString) {
- return this.searchNotesInSubtree(searchString)[0];
- }
- cloneTo(parentNoteId) {
- const cloningService = require("../../services/cloning");
- const branch = this.becca.getNote(parentNoteId).getParentBranches()[0];
- return cloningService.cloneNoteToBranch(this.noteId, branch.branchId);
- }
- decrypt() {
- if (this.isProtected && !this.isDecrypted && protectedSessionService.isProtectedSessionAvailable()) {
- try {
- this.title = protectedSessionService.decryptString(this.title);
- this.isDecrypted = true;
- }
- catch (e) {
- log.error(`Could not decrypt note ${this.noteId}: ${e.message} ${e.stack}`);
- }
- }
- }
- get isDeleted() {
- return !(this.noteId in this.becca.notes);
- }
- beforeSaving() {
- super.beforeSaving();
- this.becca.addNote(this.noteId, this);
- this.dateModified = dateUtils.localNowDateTime();
- this.utcDateModified = dateUtils.utcNowDateTime();
- }
- getPojo() {
- return {
- noteId: this.noteId,
- title: this.title,
- isProtected: this.isProtected,
- type: this.type,
- mime: this.mime,
- isDeleted: false,
- dateCreated: this.dateCreated,
- dateModified: this.dateModified,
- utcDateCreated: this.utcDateCreated,
- utcDateModified: this.utcDateModified
- };
- }
- getPojoToSave() {
- const pojo = this.getPojo();
- if (pojo.isProtected) {
- if (this.isDecrypted) {
- pojo.title = protectedSessionService.encrypt(pojo.title);
- }
- else {
- // updating protected note outside of protected session means we will keep original ciphertexts
- delete pojo.title;
- }
- }
- return pojo;
- }
- }
- module.exports = Note;
- </code></pre>
- </article>
- </section>
- </div>
- <nav>
- <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-sql.html">sql</a></li></ul><h3>Classes</h3><ul><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="EtapiToken.html">EtapiToken</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
- </nav>
- <br class="clear">
- <footer>
- Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.7</a>
- </footer>
- <script> prettyPrint(); </script>
- <script src="scripts/linenumber.js"> </script>
- </body>
- </html>
|