entities_note_short.js.html 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>JSDoc: Source: entities/note_short.js</title>
  6. <script src="scripts/prettify/prettify.js"> </script>
  7. <script src="scripts/prettify/lang-css.js"> </script>
  8. <!--[if lt IE 9]>
  9. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  10. <![endif]-->
  11. <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
  12. <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
  13. </head>
  14. <body>
  15. <div id="main">
  16. <h1 class="page-title">Source: entities/note_short.js</h1>
  17. <section>
  18. <article>
  19. <pre class="prettyprint source linenums"><code>import server from '../services/server.js';
  20. import noteAttributeCache from "../services/note_attribute_cache.js";
  21. import ws from "../services/ws.js";
  22. import options from "../services/options.js";
  23. import froca from "../services/froca.js";
  24. const LABEL = 'label';
  25. const RELATION = 'relation';
  26. const NOTE_TYPE_ICONS = {
  27. "file": "bx bx-file",
  28. "image": "bx bx-image",
  29. "code": "bx bx-code",
  30. "render": "bx bx-extension",
  31. "search": "bx bx-file-find",
  32. "relation-map": "bx bx-map-alt",
  33. "book": "bx bx-book",
  34. "note-map": "bx bx-map-alt",
  35. "mermaid": "bx bx-selection"
  36. };
  37. /**
  38. * FIXME: since there's no "full note" anymore we can rename this to Note
  39. *
  40. * This note's representation is used in note tree and is kept in Froca.
  41. */
  42. class NoteShort {
  43. /**
  44. * @param {Froca} froca
  45. * @param {Object.&lt;string, Object>} row
  46. */
  47. constructor(froca, row) {
  48. this.froca = froca;
  49. /** @type {string[]} */
  50. this.attributes = [];
  51. /** @type {string[]} */
  52. this.targetRelations = [];
  53. /** @type {string[]} */
  54. this.parents = [];
  55. /** @type {string[]} */
  56. this.children = [];
  57. /** @type {Object.&lt;string, string>} */
  58. this.parentToBranch = {};
  59. /** @type {Object.&lt;string, string>} */
  60. this.childToBranch = {};
  61. this.update(row);
  62. }
  63. update(row) {
  64. /** @type {string} */
  65. this.noteId = row.noteId;
  66. /** @type {string} */
  67. this.title = row.title;
  68. /** @type {boolean} */
  69. this.isProtected = !!row.isProtected;
  70. /**
  71. * one of 'text', 'code', 'file' or 'render'
  72. * @type {string}
  73. */
  74. this.type = row.type;
  75. /**
  76. * content-type, e.g. "application/json"
  77. * @type {string}
  78. */
  79. this.mime = row.mime;
  80. }
  81. addParent(parentNoteId, branchId) {
  82. if (parentNoteId === 'none') {
  83. return;
  84. }
  85. if (!this.parents.includes(parentNoteId)) {
  86. this.parents.push(parentNoteId);
  87. }
  88. this.parentToBranch[parentNoteId] = branchId;
  89. }
  90. addChild(childNoteId, branchId, sort = true) {
  91. if (!(childNoteId in this.childToBranch)) {
  92. this.children.push(childNoteId);
  93. }
  94. this.childToBranch[childNoteId] = branchId;
  95. if (sort) {
  96. this.sortChildren();
  97. }
  98. }
  99. sortChildren() {
  100. const branchIdPos = {};
  101. for (const branchId of Object.values(this.childToBranch)) {
  102. branchIdPos[branchId] = this.froca.getBranch(branchId).notePosition;
  103. }
  104. this.children.sort((a, b) => branchIdPos[this.childToBranch[a]] &lt; branchIdPos[this.childToBranch[b]] ? -1 : 1);
  105. }
  106. /** @returns {boolean} */
  107. isJson() {
  108. return this.mime === "application/json";
  109. }
  110. async getContent() {
  111. // we're not caching content since these objects are in froca and as such pretty long lived
  112. const note = await server.get("notes/" + this.noteId);
  113. return note.content;
  114. }
  115. async getJsonContent() {
  116. const content = await this.getContent();
  117. try {
  118. return JSON.parse(content);
  119. }
  120. catch (e) {
  121. console.log(`Cannot parse content of note ${this.noteId}: `, e.message);
  122. return null;
  123. }
  124. }
  125. /**
  126. * @returns {string[]}
  127. */
  128. getParentBranchIds() {
  129. return Object.values(this.parentToBranch);
  130. }
  131. /**
  132. * @returns {string[]}
  133. * @deprecated use getParentBranchIds() instead
  134. */
  135. getBranchIds() {
  136. return this.getParentBranchIds();
  137. }
  138. /**
  139. * @returns {Branch[]}
  140. */
  141. getParentBranches() {
  142. const branchIds = Object.values(this.parentToBranch);
  143. return this.froca.getBranches(branchIds);
  144. }
  145. /**
  146. * @returns {Branch[]}
  147. * @deprecated use getParentBranches() instead
  148. */
  149. getBranches() {
  150. return this.getParentBranches();
  151. }
  152. /** @returns {boolean} */
  153. hasChildren() {
  154. return this.children.length > 0;
  155. }
  156. /** @returns {Branch[]} */
  157. getChildBranches() {
  158. // don't use Object.values() to guarantee order
  159. const branchIds = this.children.map(childNoteId => this.childToBranch[childNoteId]);
  160. return this.froca.getBranches(branchIds);
  161. }
  162. /** @returns {string[]} */
  163. getParentNoteIds() {
  164. return this.parents;
  165. }
  166. /** @returns {NoteShort[]} */
  167. getParentNotes() {
  168. return this.froca.getNotesFromCache(this.parents);
  169. }
  170. // will sort the parents so that non-search &amp; non-archived are first and archived at the end
  171. // this is done so that non-search &amp; non-archived paths are always explored as first when looking for note path
  172. resortParents() {
  173. this.parents.sort((aNoteId, bNoteId) => {
  174. const aBranchId = this.parentToBranch[aNoteId];
  175. if (aBranchId &amp;&amp; aBranchId.startsWith('virt-')) {
  176. return 1;
  177. }
  178. const aNote = this.froca.getNoteFromCache([aNoteId]);
  179. if (aNote.hasLabel('archived')) {
  180. return 1;
  181. }
  182. return -1;
  183. });
  184. }
  185. /** @returns {string[]} */
  186. getChildNoteIds() {
  187. return this.children;
  188. }
  189. /** @returns {Promise&lt;NoteShort[]>} */
  190. async getChildNotes() {
  191. return await this.froca.getNotes(this.children);
  192. }
  193. /**
  194. * @param {string} [type] - (optional) attribute type to filter
  195. * @param {string} [name] - (optional) attribute name to filter
  196. * @returns {Attribute[]} all note's attributes, including inherited ones
  197. */
  198. getOwnedAttributes(type, name) {
  199. const attrs = this.attributes
  200. .map(attributeId => this.froca.attributes[attributeId])
  201. .filter(Boolean); // filter out nulls;
  202. return this.__filterAttrs(attrs, type, name);
  203. }
  204. /**
  205. * @param {string} [type] - (optional) attribute type to filter
  206. * @param {string} [name] - (optional) attribute name to filter
  207. * @returns {Attribute[]} all note's attributes, including inherited ones
  208. */
  209. getAttributes(type, name) {
  210. return this.__filterAttrs(this.__getCachedAttributes([]), type, name);
  211. }
  212. __getCachedAttributes(path) {
  213. // notes/clones cannot form tree cycles, it is possible to create attribute inheritance cycle via templates
  214. // when template instance is a parent of template itself
  215. if (path.includes(this.noteId)) {
  216. return [];
  217. }
  218. if (!(this.noteId in noteAttributeCache.attributes)) {
  219. const newPath = [...path, this.noteId];
  220. const attrArrs = [ this.getOwnedAttributes() ];
  221. if (this.noteId !== 'root') {
  222. for (const parentNote of this.getParentNotes()) {
  223. // these virtual parent-child relationships are also loaded into froca
  224. if (parentNote.type !== 'search') {
  225. attrArrs.push(parentNote.__getInheritableAttributes(newPath));
  226. }
  227. }
  228. }
  229. for (const templateAttr of attrArrs.flat().filter(attr => attr.type === 'relation' &amp;&amp; attr.name === 'template')) {
  230. const templateNote = this.froca.notes[templateAttr.value];
  231. if (templateNote &amp;&amp; templateNote.noteId !== this.noteId) {
  232. attrArrs.push(templateNote.__getCachedAttributes(newPath));
  233. }
  234. }
  235. noteAttributeCache.attributes[this.noteId] = [];
  236. const addedAttributeIds = new Set();
  237. for (const attr of attrArrs.flat()) {
  238. if (!addedAttributeIds.has(attr.attributeId)) {
  239. addedAttributeIds.add(attr.attributeId);
  240. noteAttributeCache.attributes[this.noteId].push(attr);
  241. }
  242. }
  243. }
  244. return noteAttributeCache.attributes[this.noteId];
  245. }
  246. isRoot() {
  247. return this.noted
  248. }
  249. getAllNotePaths(encounteredNoteIds = null) {
  250. if (this.noteId === 'root') {
  251. return [['root']];
  252. }
  253. if (!encounteredNoteIds) {
  254. encounteredNoteIds = new Set();
  255. }
  256. encounteredNoteIds.add(this.noteId);
  257. const parentNotes = this.getParentNotes();
  258. let paths;
  259. if (parentNotes.length === 1) { // optimization for the most common case
  260. if (encounteredNoteIds.has(parentNotes[0].noteId)) {
  261. return [];
  262. }
  263. else {
  264. paths = parentNotes[0].getAllNotePaths(encounteredNoteIds);
  265. }
  266. }
  267. else {
  268. paths = [];
  269. for (const parentNote of parentNotes) {
  270. if (encounteredNoteIds.has(parentNote.noteId)) {
  271. continue;
  272. }
  273. const newSet = new Set(encounteredNoteIds);
  274. paths.push(...parentNote.getAllNotePaths(newSet));
  275. }
  276. }
  277. for (const path of paths) {
  278. path.push(this.noteId);
  279. }
  280. return paths;
  281. }
  282. getSortedNotePaths(hoistedNotePath = 'root') {
  283. const notePaths = this.getAllNotePaths().map(path => ({
  284. notePath: path,
  285. isInHoistedSubTree: path.includes(hoistedNotePath),
  286. isArchived: path.find(noteId => froca.notes[noteId].hasLabel('archived')),
  287. isSearch: path.find(noteId => froca.notes[noteId].type === 'search'),
  288. isHidden: path.includes("hidden")
  289. }));
  290. notePaths.sort((a, b) => {
  291. if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
  292. return a.isInHoistedSubTree ? -1 : 1;
  293. } else if (a.isSearch !== b.isSearch) {
  294. return a.isSearch ? 1 : -1;
  295. } else if (a.isArchived !== b.isArchived) {
  296. return a.isArchived ? 1 : -1;
  297. } else {
  298. return a.notePath.length - b.notePath.length;
  299. }
  300. });
  301. return notePaths;
  302. }
  303. __filterAttrs(attributes, type, name) {
  304. if (!type &amp;&amp; !name) {
  305. return attributes;
  306. } else if (type &amp;&amp; name) {
  307. return attributes.filter(attr => attr.type === type &amp;&amp; attr.name === name);
  308. } else if (type) {
  309. return attributes.filter(attr => attr.type === type);
  310. } else if (name) {
  311. return attributes.filter(attr => attr.name === name);
  312. }
  313. }
  314. __getInheritableAttributes(path) {
  315. const attrs = this.__getCachedAttributes(path);
  316. return attrs.filter(attr => attr.isInheritable);
  317. }
  318. /**
  319. * @param {string} [name] - label name to filter
  320. * @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones
  321. */
  322. getOwnedLabels(name) {
  323. return this.getOwnedAttributes(LABEL, name);
  324. }
  325. /**
  326. * @param {string} [name] - label name to filter
  327. * @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones
  328. */
  329. getLabels(name) {
  330. return this.getAttributes(LABEL, name);
  331. }
  332. getIcon() {
  333. const iconClassLabels = this.getLabels('iconClass');
  334. const workspaceIconClass = this.getWorkspaceIconClass();
  335. if (iconClassLabels.length > 0) {
  336. return iconClassLabels[0].value;
  337. }
  338. else if (workspaceIconClass) {
  339. return workspaceIconClass;
  340. }
  341. else if (this.noteId === 'root') {
  342. return "bx bx-chevrons-right";
  343. }
  344. if (this.noteId === 'share') {
  345. return "bx bx-share-alt";
  346. }
  347. else if (this.type === 'text') {
  348. if (this.isFolder()) {
  349. return "bx bx-folder";
  350. }
  351. else {
  352. return "bx bx-note";
  353. }
  354. }
  355. else if (this.type === 'code' &amp;&amp; this.mime.startsWith('text/x-sql')) {
  356. return "bx bx-data";
  357. }
  358. else {
  359. return NOTE_TYPE_ICONS[this.type];
  360. }
  361. }
  362. isFolder() {
  363. return this.type === 'search'
  364. || this.getFilteredChildBranches().length > 0;
  365. }
  366. getFilteredChildBranches() {
  367. let childBranches = this.getChildBranches();
  368. if (!childBranches) {
  369. ws.logError(`No children for ${parentNote}. This shouldn't happen.`);
  370. return;
  371. }
  372. if (options.is("hideIncludedImages_main")) {
  373. const imageLinks = this.getRelations('imageLink');
  374. // image is already visible in the parent note so no need to display it separately in the book
  375. childBranches = childBranches.filter(branch => !imageLinks.find(rel => rel.value === branch.noteId));
  376. }
  377. // we're not checking hideArchivedNotes since that would mean we need to lazy load the child notes
  378. // which would seriously slow down everything.
  379. // we check this flag only once user chooses to expand the parent. This has the negative consequence that
  380. // note may appear as folder but not contain any children when all of them are archived
  381. return childBranches;
  382. }
  383. /**
  384. * @param {string} [name] - relation name to filter
  385. * @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones
  386. */
  387. getOwnedRelations(name) {
  388. return this.getOwnedAttributes(RELATION, name);
  389. }
  390. /**
  391. * @param {string} [name] - relation name to filter
  392. * @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones
  393. */
  394. getRelations(name) {
  395. return this.getAttributes(RELATION, name);
  396. }
  397. /**
  398. * @param {string} type - attribute type (label, relation, etc.)
  399. * @param {string} name - attribute name
  400. * @returns {boolean} true if note has an attribute with given type and name (including inherited)
  401. */
  402. hasAttribute(type, name) {
  403. return !!this.getAttribute(type, name);
  404. }
  405. /**
  406. * @param {string} type - attribute type (label, relation, etc.)
  407. * @param {string} name - attribute name
  408. * @returns {boolean} true if note has an attribute with given type and name (including inherited)
  409. */
  410. hasOwnedAttribute(type, name) {
  411. return !!this.getOwnedAttribute(type, name);
  412. }
  413. /**
  414. * @param {string} type - attribute type (label, relation, etc.)
  415. * @param {string} name - attribute name
  416. * @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.
  417. */
  418. getOwnedAttribute(type, name) {
  419. const attributes = this.getOwnedAttributes(type, name);
  420. return attributes.length > 0 ? attributes[0] : 0;
  421. }
  422. /**
  423. * @param {string} type - attribute type (label, relation, etc.)
  424. * @param {string} name - attribute name
  425. * @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.
  426. */
  427. getAttribute(type, name) {
  428. const attributes = this.getAttributes(type, name);
  429. return attributes.length > 0 ? attributes[0] : null;
  430. }
  431. /**
  432. * @param {string} type - attribute type (label, relation, etc.)
  433. * @param {string} name - attribute name
  434. * @returns {string} attribute value of given type and name or null if no such attribute exists.
  435. */
  436. getOwnedAttributeValue(type, name) {
  437. const attr = this.getOwnedAttribute(type, name);
  438. return attr ? attr.value : null;
  439. }
  440. /**
  441. * @param {string} type - attribute type (label, relation, etc.)
  442. * @param {string} name - attribute name
  443. * @returns {string} attribute value of given type and name or null if no such attribute exists.
  444. */
  445. getAttributeValue(type, name) {
  446. const attr = this.getAttribute(type, name);
  447. return attr ? attr.value : null;
  448. }
  449. /**
  450. * @param {string} name - label name
  451. * @returns {boolean} true if label exists (excluding inherited)
  452. */
  453. hasOwnedLabel(name) { return this.hasOwnedAttribute(LABEL, name); }
  454. /**
  455. * @param {string} name - label name
  456. * @returns {boolean} true if label exists (including inherited)
  457. */
  458. hasLabel(name) { return this.hasAttribute(LABEL, name); }
  459. /**
  460. * @param {string} name - relation name
  461. * @returns {boolean} true if relation exists (excluding inherited)
  462. */
  463. hasOwnedRelation(name) { return this.hasOwnedAttribute(RELATION, name); }
  464. /**
  465. * @param {string} name - relation name
  466. * @returns {boolean} true if relation exists (including inherited)
  467. */
  468. hasRelation(name) { return this.hasAttribute(RELATION, name); }
  469. /**
  470. * @param {string} name - label name
  471. * @returns {Attribute} label if it exists, null otherwise
  472. */
  473. getOwnedLabel(name) { return this.getOwnedAttribute(LABEL, name); }
  474. /**
  475. * @param {string} name - label name
  476. * @returns {Attribute} label if it exists, null otherwise
  477. */
  478. getLabel(name) { return this.getAttribute(LABEL, name); }
  479. /**
  480. * @param {string} name - relation name
  481. * @returns {Attribute} relation if it exists, null otherwise
  482. */
  483. getOwnedRelation(name) { return this.getOwnedAttribute(RELATION, name); }
  484. /**
  485. * @param {string} name - relation name
  486. * @returns {Attribute} relation if it exists, null otherwise
  487. */
  488. getRelation(name) { return this.getAttribute(RELATION, name); }
  489. /**
  490. * @param {string} name - label name
  491. * @returns {string} label value if label exists, null otherwise
  492. */
  493. getOwnedLabelValue(name) { return this.getOwnedAttributeValue(LABEL, name); }
  494. /**
  495. * @param {string} name - label name
  496. * @returns {string} label value if label exists, null otherwise
  497. */
  498. getLabelValue(name) { return this.getAttributeValue(LABEL, name); }
  499. /**
  500. * @param {string} name - relation name
  501. * @returns {string} relation value if relation exists, null otherwise
  502. */
  503. getOwnedRelationValue(name) { return this.getOwnedAttributeValue(RELATION, name); }
  504. /**
  505. * @param {string} name - relation name
  506. * @returns {string} relation value if relation exists, null otherwise
  507. */
  508. getRelationValue(name) { return this.getAttributeValue(RELATION, name); }
  509. /**
  510. * @param {string} name
  511. * @returns {Promise&lt;NoteShort>|null} target note of the relation or null (if target is empty or note was not found)
  512. */
  513. async getRelationTarget(name) {
  514. const targets = await this.getRelationTargets(name);
  515. return targets.length > 0 ? targets[0] : null;
  516. }
  517. /**
  518. * @param {string} [name] - relation name to filter
  519. * @returns {Promise&lt;NoteShort[]>}
  520. */
  521. async getRelationTargets(name) {
  522. const relations = this.getRelations(name);
  523. const targets = [];
  524. for (const relation of relations) {
  525. targets.push(await this.froca.getNote(relation.value));
  526. }
  527. return targets;
  528. }
  529. /**
  530. * @returns {NoteShort[]}
  531. */
  532. getTemplateNotes() {
  533. const relations = this.getRelations('template');
  534. return relations.map(rel => this.froca.notes[rel.value]);
  535. }
  536. getPromotedDefinitionAttributes() {
  537. if (this.hasLabel('hidePromotedAttributes')) {
  538. return [];
  539. }
  540. return this.getAttributes()
  541. .filter(attr => attr.isDefinition())
  542. .filter(attr => {
  543. const def = attr.getDefinition();
  544. return def &amp;&amp; def.isPromoted;
  545. });
  546. }
  547. hasAncestor(ancestorNoteId, visitedNoteIds = null) {
  548. if (this.noteId === ancestorNoteId) {
  549. return true;
  550. }
  551. if (!visitedNoteIds) {
  552. visitedNoteIds = new Set();
  553. } else if (visitedNoteIds.has(this.noteId)) {
  554. // to avoid infinite cycle when template is descendent of the instance
  555. return false;
  556. }
  557. visitedNoteIds.add(this.noteId);
  558. for (const templateNote of this.getTemplateNotes()) {
  559. if (templateNote.hasAncestor(ancestorNoteId, visitedNoteIds)) {
  560. return true;
  561. }
  562. }
  563. for (const parentNote of this.getParentNotes()) {
  564. if (parentNote.hasAncestor(ancestorNoteId, visitedNoteIds)) {
  565. return true;
  566. }
  567. }
  568. return false;
  569. }
  570. /**
  571. * @deprecated NOOP
  572. */
  573. invalidateAttributeCache() {}
  574. /**
  575. * Get relations which target this note
  576. *
  577. * @returns {Attribute[]}
  578. */
  579. getTargetRelations() {
  580. return this.targetRelations
  581. .map(attributeId => this.froca.attributes[attributeId]);
  582. }
  583. /**
  584. * Get relations which target this note
  585. *
  586. * @returns {NoteShort[]}
  587. */
  588. async getTargetRelationSourceNotes() {
  589. const targetRelations = this.getTargetRelations();
  590. return await this.froca.getNotes(targetRelations.map(tr => tr.noteId));
  591. }
  592. /**
  593. * Return note complement which is most importantly note's content
  594. *
  595. * @return {Promise&lt;NoteComplement>}
  596. */
  597. async getNoteComplement() {
  598. return await this.froca.getNoteComplement(this.noteId);
  599. }
  600. toString() {
  601. return `Note(noteId=${this.noteId}, title=${this.title})`;
  602. }
  603. get dto() {
  604. const dto = Object.assign({}, this);
  605. delete dto.froca;
  606. return dto;
  607. }
  608. getCssClass() {
  609. const labels = this.getLabels('cssClass');
  610. return labels.map(l => l.value).join(' ');
  611. }
  612. getWorkspaceIconClass() {
  613. const labels = this.getLabels('workspaceIconClass');
  614. return labels.length > 0 ? labels[0].value : "";
  615. }
  616. getWorkspaceTabBackgroundColor() {
  617. const labels = this.getLabels('workspaceTabBackgroundColor');
  618. return labels.length > 0 ? labels[0].value : "";
  619. }
  620. /** @returns {boolean} true if this note is JavaScript (code or attachment) */
  621. isJavaScript() {
  622. return (this.type === "code" || this.type === "file")
  623. &amp;&amp; (this.mime.startsWith("application/javascript")
  624. || this.mime === "application/x-javascript"
  625. || this.mime === "text/javascript");
  626. }
  627. /** @returns {boolean} true if this note is HTML */
  628. isHtml() {
  629. return (this.type === "code" || this.type === "file" || this.type === "render") &amp;&amp; this.mime === "text/html";
  630. }
  631. /** @returns {string|null} JS script environment - either "frontend" or "backend" */
  632. getScriptEnv() {
  633. if (this.isHtml() || (this.isJavaScript() &amp;&amp; this.mime.endsWith('env=frontend'))) {
  634. return "frontend";
  635. }
  636. if (this.type === 'render') {
  637. return "frontend";
  638. }
  639. if (this.isJavaScript() &amp;&amp; this.mime.endsWith('env=backend')) {
  640. return "backend";
  641. }
  642. return null;
  643. }
  644. async executeScript() {
  645. if (!this.isJavaScript()) {
  646. throw new Error(`Note ${this.noteId} is of type ${this.type} and mime ${this.mime} and thus cannot be executed`);
  647. }
  648. const env = this.getScriptEnv();
  649. if (env === "frontend") {
  650. const bundleService = (await import("../services/bundle.js")).default;
  651. await bundleService.getAndExecuteBundle(this.noteId);
  652. }
  653. else if (env === "backend") {
  654. await server.post('script/run/' + this.noteId);
  655. }
  656. else {
  657. throw new Error(`Unrecognized env type ${env} for note ${this.noteId}`);
  658. }
  659. }
  660. isShared() {
  661. for (const parentNoteId of this.parents) {
  662. if (parentNoteId === 'root' || parentNoteId === 'none') {
  663. continue;
  664. }
  665. const parentNote = froca.notes[parentNoteId];
  666. if (!parentNote || parentNote.type === 'search') {
  667. continue;
  668. }
  669. if (parentNote.noteId === 'share' || parentNote.isShared()) {
  670. return true;
  671. }
  672. }
  673. return false;
  674. }
  675. }
  676. export default NoteShort;
  677. </code></pre>
  678. </article>
  679. </section>
  680. </div>
  681. <nav>
  682. <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Attribute.html">Attribute</a></li><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#doRenderBody">doRenderBody</a></li></ul>
  683. </nav>
  684. <br class="clear">
  685. <footer>
  686. Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.7</a>
  687. </footer>
  688. <script> prettyPrint(); </script>
  689. <script src="scripts/linenumber.js"> </script>
  690. </body>
  691. </html>