services_frontend_script_api.js.html 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>JSDoc: Source: services/frontend_script_api.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: services/frontend_script_api.js</h1>
  17. <section>
  18. <article>
  19. <pre class="prettyprint source linenums"><code>import server from './server.js';
  20. import utils from './utils.js';
  21. import toastService from './toast.js';
  22. import linkService from './link.js';
  23. import froca from './froca.js';
  24. import noteTooltipService from './note_tooltip.js';
  25. import protectedSessionService from './protected_session.js';
  26. import dateNotesService from './date_notes.js';
  27. import searchService from './search.js';
  28. import CollapsibleWidget from '../widgets/collapsible_widget.js';
  29. import ws from "./ws.js";
  30. import appContext from "./app_context.js";
  31. import NoteContextAwareWidget from "../widgets/note_context_aware_widget.js";
  32. import NoteContextCachingWidget from "../widgets/note_context_caching_widget.js";
  33. import BasicWidget from "../widgets/basic_widget.js";
  34. /**
  35. * This is the main frontend API interface for scripts. It's published in the local "api" object.
  36. *
  37. * @constructor
  38. * @hideconstructor
  39. */
  40. function FrontendScriptApi(startNote, currentNote, originEntity = null, $container = null) {
  41. const $pluginButtons = $("#plugin-buttons");
  42. /** @property {jQuery} container of all the rendered script content */
  43. this.$container = $container;
  44. /** @property {object} note where script started executing */
  45. this.startNote = startNote;
  46. /** @property {object} note where script is currently executing */
  47. this.currentNote = currentNote;
  48. /** @property {object|null} entity whose event triggered this execution */
  49. this.originEntity = originEntity;
  50. // to keep consistency with backend API
  51. this.dayjs = dayjs;
  52. /** @property {CollapsibleWidget} */
  53. this.CollapsibleWidget = CollapsibleWidget;
  54. /**
  55. * @property {NoteContextAwareWidget}
  56. * @deprecated use NoteContextAwareWidget instead
  57. */
  58. this.TabAwareWidget = NoteContextAwareWidget;
  59. /** @property {NoteContextAwareWidget} */
  60. this.NoteContextAwareWidget = NoteContextAwareWidget;
  61. /**
  62. * @property {NoteContextCachingWidget}
  63. * @deprecated use NoteContextCachingWidget instead
  64. */
  65. this.TabCachingWidget = NoteContextCachingWidget;
  66. /** @property {NoteContextAwareWidget} */
  67. this.NoteContextCachingWidget = NoteContextCachingWidget;
  68. /** @property {BasicWidget} */
  69. this.BasicWidget = BasicWidget;
  70. /**
  71. * Activates note in the tree and in the note detail.
  72. *
  73. * @method
  74. * @param {string} notePath (or noteId)
  75. * @returns {Promise&lt;void>}
  76. */
  77. this.activateNote = async notePath => {
  78. await appContext.tabManager.getActiveContext().setNote(notePath);
  79. };
  80. /**
  81. * Activates newly created note. Compared to this.activateNote() also makes sure that frontend has been fully synced.
  82. *
  83. * @param {string} notePath (or noteId)
  84. * @return {Promise&lt;void>}
  85. */
  86. this.activateNewNote = async notePath => {
  87. await ws.waitForMaxKnownEntityChangeId();
  88. await appContext.tabManager.getActiveContext().setNote(notePath);
  89. appContext.triggerEvent('focusAndSelectTitle');
  90. };
  91. /**
  92. * Open a note in a new tab.
  93. *
  94. * @param {string} notePath (or noteId)
  95. * @param {boolean} activate - set to true to activate the new tab, false to stay on the current tab
  96. * @return {Promise&lt;void>}
  97. */
  98. this.openTabWithNote = async (notePath, activate) => {
  99. await ws.waitForMaxKnownEntityChangeId();
  100. await appContext.tabManager.openContextWithNote(notePath, activate);
  101. if (activate) {
  102. appContext.triggerEvent('focusAndSelectTitle');
  103. }
  104. };
  105. /**
  106. * @typedef {Object} ToolbarButtonOptions
  107. * @property {string} title
  108. * @property {string} [icon] - name of the boxicon to be used (e.g. "time" for "bx-time" icon)
  109. * @property {function} action - callback handling the click on the button
  110. * @property {string} [shortcut] - keyboard shortcut for the button, e.g. "alt+t"
  111. */
  112. /**
  113. * Adds new button the the plugin area.
  114. *
  115. * @param {ToolbarButtonOptions} opts
  116. */
  117. this.addButtonToToolbar = opts => {
  118. const buttonId = "toolbar-button-" + opts.title.replace(/\s/g, "-");
  119. let button;
  120. if (utils.isMobile()) {
  121. $('#plugin-buttons-placeholder').remove();
  122. button = $('&lt;a class="dropdown-item" href="#">')
  123. .on('click', () => {
  124. setTimeout(() => $pluginButtons.dropdown('hide'), 0);
  125. });
  126. if (opts.icon) {
  127. button.append($("&lt;span>").addClass("bx bx-" + opts.icon))
  128. .append("&amp;nbsp;");
  129. }
  130. button.append($("&lt;span>").text(opts.title));
  131. } else {
  132. button = $('&lt;span class="button-widget icon-action bx" data-toggle="tooltip" title="" data-placement="right">&lt;/span>')
  133. .addClass("bx bx-" + (opts.icon || "question-mark"));
  134. button.attr("title", opts.title);
  135. button.tooltip({html: true});
  136. }
  137. button = button.on('click', opts.action);
  138. button.attr('id', buttonId);
  139. if ($("#" + buttonId).replaceWith(button).length === 0) {
  140. $pluginButtons.append(button);
  141. }
  142. if (opts.shortcut) {
  143. utils.bindGlobalShortcut(opts.shortcut, opts.action);
  144. button.attr("title", "Shortcut " + opts.shortcut);
  145. }
  146. };
  147. function prepareParams(params) {
  148. if (!params) {
  149. return params;
  150. }
  151. return params.map(p => {
  152. if (typeof p === "function") {
  153. return "!@#Function: " + p.toString();
  154. }
  155. else {
  156. return p;
  157. }
  158. });
  159. }
  160. /**
  161. * Executes given anonymous function on the backend.
  162. * Internally this serializes the anonymous function into string and sends it to backend via AJAX.
  163. *
  164. * @param {string} script - script to be executed on the backend
  165. * @param {Array.&lt;?>} params - list of parameters to the anonymous function to be send to backend
  166. * @return {Promise&lt;*>} return value of the executed function on the backend
  167. */
  168. this.runOnBackend = async (script, params = []) => {
  169. if (typeof script === "function") {
  170. script = script.toString();
  171. }
  172. const ret = await server.post('script/exec', {
  173. script: script,
  174. params: prepareParams(params),
  175. startNoteId: startNote.noteId,
  176. currentNoteId: currentNote.noteId,
  177. originEntityName: "notes", // currently there's no other entity on frontend which can trigger event
  178. originEntityId: originEntity ? originEntity.noteId : null
  179. }, "script");
  180. if (ret.success) {
  181. await ws.waitForMaxKnownEntityChangeId();
  182. return ret.executionResult;
  183. }
  184. else {
  185. throw new Error("server error: " + ret.error);
  186. }
  187. };
  188. /**
  189. * @deprecated new name of this API call is runOnBackend so use that
  190. * @method
  191. */
  192. this.runOnServer = this.runOnBackend;
  193. /**
  194. * This is a powerful search method - you can search by attributes and their values, e.g.:
  195. * "#dateModified =* MONTH AND #log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
  196. *
  197. * @method
  198. * @param {string} searchString
  199. * @returns {Promise&lt;NoteShort[]>}
  200. */
  201. this.searchForNotes = async searchString => {
  202. return await searchService.searchForNotes(searchString);
  203. };
  204. /**
  205. * This is a powerful search method - you can search by attributes and their values, e.g.:
  206. * "#dateModified =* MONTH AND #log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
  207. *
  208. * @method
  209. * @param {string} searchString
  210. * @returns {Promise&lt;NoteShort|null>}
  211. */
  212. this.searchForNote = async searchString => {
  213. const notes = await this.searchForNotes(searchString);
  214. return notes.length > 0 ? notes[0] : null;
  215. };
  216. /**
  217. * Returns note by given noteId. If note is missing from cache, it's loaded.
  218. **
  219. * @param {string} noteId
  220. * @return {Promise&lt;NoteShort>}
  221. */
  222. this.getNote = async noteId => await froca.getNote(noteId);
  223. /**
  224. * Returns list of notes. If note is missing from cache, it's loaded.
  225. *
  226. * This is often used to bulk-fill the cache with notes which would have to be picked one by one
  227. * otherwise (by e.g. createNoteLink())
  228. *
  229. * @param {string[]} noteIds
  230. * @param {boolean} [silentNotFoundError] - don't report error if the note is not found
  231. * @return {Promise&lt;NoteShort[]>}
  232. */
  233. this.getNotes = async (noteIds, silentNotFoundError = false) => await froca.getNotes(noteIds, silentNotFoundError);
  234. /**
  235. * Update frontend tree (note) cache from the backend.
  236. *
  237. * @param {string[]} noteIds
  238. * @method
  239. */
  240. this.reloadNotes = async noteIds => await froca.reloadNotes(noteIds);
  241. /**
  242. * Instance name identifies particular Trilium instance. It can be useful for scripts
  243. * if some action needs to happen on only one specific instance.
  244. *
  245. * @return {string}
  246. */
  247. this.getInstanceName = () => window.glob.instanceName;
  248. /**
  249. * @method
  250. * @param {Date} date
  251. * @returns {string} date in YYYY-MM-DD format
  252. */
  253. this.formatDateISO = utils.formatDateISO;
  254. /**
  255. * @method
  256. * @param {string} str
  257. * @returns {Date} parsed object
  258. */
  259. this.parseDate = utils.parseDate;
  260. /**
  261. * Show info message to the user.
  262. *
  263. * @method
  264. * @param {string} message
  265. */
  266. this.showMessage = toastService.showMessage;
  267. /**
  268. * Show error message to the user.
  269. *
  270. * @method
  271. * @param {string} message
  272. */
  273. this.showError = toastService.showError;
  274. /**
  275. * @method
  276. * @deprecated - this is now no-op since all the changes should be gracefully handled per widget
  277. */
  278. this.refreshTree = () => {};
  279. /**
  280. * Create note link (jQuery object) for given note.
  281. *
  282. * @method
  283. * @param {string} notePath (or noteId)
  284. * @param {object} [params]
  285. * @param {boolean} [params.showTooltip=true] - enable/disable tooltip on the link
  286. * @param {boolean} [params.showNotePath=false] - show also whole note's path as part of the link
  287. * @param {string} [title=] - custom link tile with note's title as default
  288. */
  289. this.createNoteLink = linkService.createNoteLink;
  290. /**
  291. * Adds given text to the editor cursor
  292. *
  293. * @param {string} text - this must be clear text, HTML is not supported.
  294. * @method
  295. */
  296. this.addTextToActiveTabEditor = text => appContext.triggerCommand('addTextToActiveEditor', {text});
  297. /**
  298. * @method
  299. * @returns {NoteShort} active note (loaded into right pane)
  300. */
  301. this.getActiveTabNote = () => appContext.tabManager.getActiveContextNote();
  302. /**
  303. * See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
  304. *
  305. * @method
  306. * @param callback - method receiving "textEditor" instance
  307. */
  308. this.getActiveTabTextEditor = callback => appContext.triggerCommand('executeInActiveEditor', {callback});
  309. /**
  310. * @method
  311. * @returns {Promise&lt;string|null>} returns note path of active note or null if there isn't active note
  312. */
  313. this.getActiveTabNotePath = () => appContext.tabManager.getActiveContextNotePath();
  314. /**
  315. * @method
  316. * @param {object} $el - jquery object on which to setup the tooltip
  317. */
  318. this.setupElementTooltip = noteTooltipService.setupElementTooltip;
  319. /**
  320. * @deprecated use protectNote and protectSubtree instead
  321. * @method
  322. */
  323. this.protectActiveNote = async () => {
  324. const activeNote = appContext.tabManager.getActiveContextNote();
  325. await protectedSessionService.protectNote(activeNote.noteId, true, false);
  326. };
  327. /**
  328. * @method
  329. * @param {string} noteId
  330. * @param {boolean} protect - true to protect note, false to unprotect
  331. */
  332. this.protectNote = async (noteId, protect) => {
  333. await protectedSessionService.protectNote(noteId, protect, false);
  334. };
  335. /**
  336. * @method
  337. * @param {string} noteId
  338. * @param {boolean} protect - true to protect subtree, false to unprotect
  339. */
  340. this.protectSubTree = async (noteId, protect) => {
  341. await protectedSessionService.protectNote(noteId, protect, true);
  342. };
  343. /**
  344. * Returns date-note for today. If it doesn't exist, it is automatically created.
  345. *
  346. * @method
  347. * @return {Promise&lt;NoteShort>}
  348. */
  349. this.getTodayNote = dateNotesService.getTodayNote;
  350. /**
  351. * Returns date-note. If it doesn't exist, it is automatically created.
  352. *
  353. * @method
  354. * @param {string} date - e.g. "2019-04-29"
  355. * @return {Promise&lt;NoteShort>}
  356. */
  357. this.getDateNote = dateNotesService.getDateNote;
  358. /**
  359. * Returns date-note for the first date of the week of the given date. If it doesn't exist, it is automatically created.
  360. *
  361. * @method
  362. * @param {string} date - e.g. "2019-04-29"
  363. * @return {Promise&lt;NoteShort>}
  364. */
  365. this.getWeekNote = dateNotesService.getWeekNote;
  366. /**
  367. * Returns month-note. If it doesn't exist, it is automatically created.
  368. *
  369. * @method
  370. * @param {string} month - e.g. "2019-04"
  371. * @return {Promise&lt;NoteShort>}
  372. */
  373. this.getMonthNote = dateNotesService.getMonthNote;
  374. /**
  375. * Returns year-note. If it doesn't exist, it is automatically created.
  376. *
  377. * @method
  378. * @param {string} year - e.g. "2019"
  379. * @return {Promise&lt;NoteShort>}
  380. */
  381. this.getYearNote = dateNotesService.getYearNote;
  382. /**
  383. * Hoist note in the current tab. See https://github.com/zadam/trilium/wiki/Note-hoisting
  384. *
  385. * @method
  386. * @param {string} noteId - set hoisted note. 'root' will effectively unhoist
  387. * @return {Promise}
  388. */
  389. this.setHoistedNoteId = (noteId) => {
  390. const activeNoteContext = appContext.tabManager.getActiveContext();
  391. if (activeNoteContext) {
  392. activeNoteContext.setHoistedNoteId(noteId);
  393. }
  394. };
  395. /**
  396. * @method
  397. * @param {string} keyboardShortcut - e.g. "ctrl+shift+a"
  398. * @param {function} handler
  399. */
  400. this.bindGlobalShortcut = utils.bindGlobalShortcut;
  401. /**
  402. * Trilium runs in backend and frontend process, when something is changed on the backend from script,
  403. * frontend will get asynchronously synchronized.
  404. *
  405. * This method returns a promise which resolves once all the backend -> frontend synchronization is finished.
  406. * Typical use case is when new note has been created, we should wait until it is synced into frontend and only then activate it.
  407. *
  408. * @method
  409. */
  410. this.waitUntilSynced = ws.waitForMaxKnownEntityChangeId;
  411. /**
  412. * This will refresh all currently opened notes which have included note specified in the parameter
  413. *
  414. * @param includedNoteId - noteId of the included note
  415. */
  416. this.refreshIncludedNote = includedNoteId => appContext.triggerEvent('refreshIncludedNote', {noteId: includedNoteId});
  417. /**
  418. * Return randomly generated string of given length. This random string generation is NOT cryptographically secure.
  419. *
  420. * @method
  421. * @param {number} length of the string
  422. * @returns {string} random string
  423. */
  424. this.randomString = utils.randomString;
  425. }
  426. export default FrontendScriptApi;
  427. </code></pre>
  428. </article>
  429. </section>
  430. </div>
  431. <nav>
  432. <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>
  433. </nav>
  434. <br class="clear">
  435. <footer>
  436. Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.7</a>
  437. </footer>
  438. <script> prettyPrint(); </script>
  439. <script src="scripts/linenumber.js"> </script>
  440. </body>
  441. </html>