From 7f0c92c56b44565d9606fcf1f19368e9a42e700f Mon Sep 17 00:00:00 2001 From: zadam Date: Sat, 4 May 2019 22:44:25 +0200 Subject: [PATCH] attributes and children overview working again --- package-lock.json | 2 +- src/public/javascripts/desktop.js | 2 - src/public/javascripts/dialogs/attributes.js | 5 +- src/public/javascripts/services/attributes.js | 498 +++++++++--------- .../javascripts/services/note_context.js | 14 +- .../javascripts/services/note_detail.js | 18 +- .../javascripts/services/note_detail_image.js | 9 +- .../javascripts/services/note_detail_text.js | 4 +- src/public/javascripts/services/note_type.js | 56 +- src/public/stylesheets/style.css | 2 +- 10 files changed, 300 insertions(+), 310 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0d150f90..74215005d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "trilium", - "version": "0.31.4", + "version": "0.31.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/public/javascripts/desktop.js b/src/public/javascripts/desktop.js index a08e51c7e..c34240254 100644 --- a/src/public/javascripts/desktop.js +++ b/src/public/javascripts/desktop.js @@ -152,8 +152,6 @@ noteTooltipService.setupGlobalTooltip(); bundle.executeStartupBundles(); -noteTypeService.init(); - linkService.init(); noteAutocompleteService.init(); diff --git a/src/public/javascripts/dialogs/attributes.js b/src/public/javascripts/dialogs/attributes.js index 550d3b562..1d477b815 100644 --- a/src/public/javascripts/dialogs/attributes.js +++ b/src/public/javascripts/dialogs/attributes.js @@ -2,7 +2,6 @@ import noteDetailService from '../services/note_detail.js'; import server from '../services/server.js'; import infoService from "../services/info.js"; import treeUtils from "../services/tree_utils.js"; -import attributeService from "../services/attributes.js"; import attributeAutocompleteService from "../services/attribute_autocomplete.js"; const $dialog = $("#attributes-dialog"); @@ -168,7 +167,9 @@ function AttributesModel() { infoService.showMessage("Attributes have been saved."); - attributeService.refreshAttributes(); + const ctx = noteDetailService.getActiveContext(); + + ctx.attributes.refreshAttributes(); noteDetailService.reload(); }; diff --git a/src/public/javascripts/services/attributes.js b/src/public/javascripts/services/attributes.js index 1643c0d49..be6b34aa5 100644 --- a/src/public/javascripts/services/attributes.js +++ b/src/public/javascripts/services/attributes.js @@ -4,308 +4,306 @@ import messagingService from "./messaging.js"; import treeUtils from "./tree_utils.js"; import noteAutocompleteService from "./note_autocomplete.js"; import linkService from "./link.js"; -import noteDetailService from "./note_detail.js"; -const $attributeList = $("#attribute-list"); -const $attributeListInner = $("#attribute-list-inner"); -const $promotedAttributesContainer = $("#note-detail-promoted-attributes"); -const $savedIndicator = $(".saved-indicator"); - -let attributePromise; - -function invalidateAttributes() { - attributePromise = null; -} - -function reloadAttributes() { - attributePromise = server.get('notes/' + noteDetailService.getActiveNoteId() + '/attributes'); -} - -async function refreshAttributes() { - reloadAttributes(); - - await showAttributes(); -} - -async function getAttributes() { - if (!attributePromise) { - reloadAttributes(); +class Attributes { + /** + * @param {NoteContext} ctx + */ + constructor(ctx) { + this.ctx = ctx; + this.$attributeList = ctx.$noteTabContent.find(".attribute-list"); + this.$attributeListInner = ctx.$noteTabContent.find(".attribute-list-inner"); + this.$promotedAttributesContainer = ctx.$noteTabContent.find(".note-detail-promoted-attributes"); + this.$savedIndicator = ctx.$noteTabContent.find(".saved-indicator"); + this.attributePromise = null; } - return await attributePromise; -} + invalidateAttributes() { + this.attributePromise = null; + } -async function showAttributes() { - // FIXME tabs - return; + reloadAttributes() { + this.attributePromise = server.get(`notes/${this.ctx.note.noteId}/attributes`); + } - $promotedAttributesContainer.empty(); - $attributeList.hide(); - $attributeListInner.empty(); + async refreshAttributes() { + this.reloadAttributes(); - const note = noteDetailService.getActiveNote(); + await this.showAttributes(); + } - const attributes = await attributePromise; - - const promoted = attributes.filter(attr => - (attr.type === 'label-definition' || attr.type === 'relation-definition') - && !attr.name.startsWith("child:") - && attr.value.isPromoted); - - const hidePromotedAttributes = attributes.some(attr => attr.type === 'label' && attr.name === 'hidePromotedAttributes'); - - if (promoted.length > 0 && !hidePromotedAttributes) { - const $tbody = $(""); - - for (const definitionAttr of promoted) { - const definitionType = definitionAttr.type; - const valueType = definitionType.substr(0, definitionType.length - 11); - - let valueAttrs = attributes.filter(el => el.name === definitionAttr.name && el.type === valueType); - - if (valueAttrs.length === 0) { - valueAttrs.push({ - attributeId: "", - type: valueType, - name: definitionAttr.name, - value: "" - }); - } - - if (definitionAttr.value.multiplicityType === 'singlevalue') { - valueAttrs = valueAttrs.slice(0, 1); - } - - for (const valueAttr of valueAttrs) { - const $tr = await createPromotedAttributeRow(definitionAttr, valueAttr); - - $tbody.append($tr); - } + async getAttributes() { + if (!this.attributePromise) { + this.reloadAttributes(); } - // we replace the whole content in one step so there can't be any race conditions - // (previously we saw promoted attributes doubling) - $promotedAttributesContainer.empty().append($tbody); + return await this.attributePromise; } - else if (note.type !== 'relation-map') { - // display only "own" notes - const ownedAttributes = attributes.filter(attr => attr.noteId === note.noteId); - if (ownedAttributes.length > 0) { - for (const attribute of ownedAttributes) { - if (attribute.type === 'label') { - $attributeListInner.append(utils.formatLabel(attribute) + " "); + async showAttributes() { + this.$promotedAttributesContainer.empty(); + this.$attributeList.hide(); + this.$attributeListInner.empty(); + + const note = this.ctx.note; + + const attributes = await this.getAttributes(); + + const promoted = attributes.filter(attr => + (attr.type === 'label-definition' || attr.type === 'relation-definition') + && !attr.name.startsWith("child:") + && attr.value.isPromoted); + + const hidePromotedAttributes = attributes.some(attr => attr.type === 'label' && attr.name === 'hidePromotedAttributes'); + + if (promoted.length > 0 && !hidePromotedAttributes) { + const $tbody = $(""); + + for (const definitionAttr of promoted) { + const definitionType = definitionAttr.type; + const valueType = definitionType.substr(0, definitionType.length - 11); + + let valueAttrs = attributes.filter(el => el.name === definitionAttr.name && el.type === valueType); + + if (valueAttrs.length === 0) { + valueAttrs.push({ + attributeId: "", + type: valueType, + name: definitionAttr.name, + value: "" + }); } - else if (attribute.type === 'relation') { - if (attribute.value) { - $attributeListInner.append('@' + attribute.name + "="); - $attributeListInner.append(await linkService.createNoteLink(attribute.value)); - $attributeListInner.append(" "); + + if (definitionAttr.value.multiplicityType === 'singlevalue') { + valueAttrs = valueAttrs.slice(0, 1); + } + + for (const valueAttr of valueAttrs) { + const $tr = await this.createPromotedAttributeRow(definitionAttr, valueAttr); + + $tbody.append($tr); + } + } + + // we replace the whole content in one step so there can't be any race conditions + // (previously we saw promoted attributes doubling) + this.$promotedAttributesContainer.empty().append($tbody); + } + else if (note.type !== 'relation-map') { + // display only "own" notes + const ownedAttributes = attributes.filter(attr => attr.noteId === note.noteId); + + if (ownedAttributes.length > 0) { + for (const attribute of ownedAttributes) { + if (attribute.type === 'label') { + this.$attributeListInner.append(utils.formatLabel(attribute) + " "); + } + else if (attribute.type === 'relation') { + if (attribute.value) { + this.$attributeListInner.append('@' + attribute.name + "="); + this.$attributeListInner.append(await linkService.createNoteLink(attribute.value)); + this.$attributeListInner.append(" "); + } + else { + messagingService.logError(`Relation ${attribute.attributeId} has empty target`); + } + } + else if (attribute.type === 'label-definition' || attribute.type === 'relation-definition') { + this.$attributeListInner.append(attribute.name + " definition "); } else { - messagingService.logError(`Relation ${attribute.attributeId} has empty target`); + messagingService.logError("Unknown attr type: " + attribute.type); } } - else if (attribute.type === 'label-definition' || attribute.type === 'relation-definition') { - $attributeListInner.append(attribute.name + " definition "); - } - else { - messagingService.logError("Unknown attr type: " + attribute.type); - } - } - $attributeList.show(); + this.$attributeList.show(); + } } + + return attributes; } - return attributes; -} + async createPromotedAttributeRow(definitionAttr, valueAttr) { + const definition = definitionAttr.value; + const $tr = $(""); + const $labelCell = $("").append(valueAttr.name); + const $input = $("") + .prop("tabindex", definitionAttr.position) + .prop("attribute-id", valueAttr.isOwned ? valueAttr.attributeId : '') // if not owned, we'll force creation of a new attribute instead of updating the inherited one + .prop("attribute-type", valueAttr.type) + .prop("attribute-name", valueAttr.name) + .prop("value", valueAttr.value) + .addClass("form-control") + .addClass("promoted-attribute-input") + .change(event => this.promotedAttributeChanged(event)); -async function createPromotedAttributeRow(definitionAttr, valueAttr) { - const definition = definitionAttr.value; - const $tr = $(""); - const $labelCell = $("").append(valueAttr.name); - const $input = $("") - .prop("tabindex", definitionAttr.position) - .prop("attribute-id", valueAttr.isOwned ? valueAttr.attributeId : '') // if not owned, we'll force creation of a new attribute instead of updating the inherited one - .prop("attribute-type", valueAttr.type) - .prop("attribute-name", valueAttr.name) - .prop("value", valueAttr.value) - .addClass("form-control") - .addClass("promoted-attribute-input") - .change(promotedAttributeChanged); + const $inputCell = $("").append($("
").addClass("input-group").append($input)); - const $inputCell = $("").append($("
").addClass("input-group").append($input)); + const $actionCell = $(""); + const $multiplicityCell = $("") + .addClass("multiplicity") + .attr("nowrap", true); - const $actionCell = $(""); - const $multiplicityCell = $("") - .addClass("multiplicity") - .attr("nowrap", true); + $tr + .append($labelCell) + .append($inputCell) + .append($actionCell) + .append($multiplicityCell); - $tr - .append($labelCell) - .append($inputCell) - .append($actionCell) - .append($multiplicityCell); + if (valueAttr.type === 'label') { + if (definition.labelType === 'text') { + $input.prop("type", "text"); - if (valueAttr.type === 'label') { - if (definition.labelType === 'text') { - $input.prop("type", "text"); + // no need to await for this, can be done asynchronously + server.get('attributes/values/' + encodeURIComponent(valueAttr.name)).then(attributeValues => { + if (attributeValues.length === 0) { + return; + } - // no need to await for this, can be done asynchronously - server.get('attributes/values/' + encodeURIComponent(valueAttr.name)).then(attributeValues => { - if (attributeValues.length === 0) { - return; + attributeValues = attributeValues.map(attribute => { return { value: attribute }; }); + + $input.autocomplete({ + appendTo: document.querySelector('body'), + hint: false, + autoselect: false, + openOnFocus: true, + minLength: 0, + tabAutocomplete: false + }, [{ + displayKey: 'value', + source: function (term, cb) { + term = term.toLowerCase(); + + const filtered = attributeValues.filter(attr => attr.value.toLowerCase().includes(term)); + + cb(filtered); + } + }]); + }); + } + else if (definition.labelType === 'number') { + $input.prop("type", "number"); + + let step = 1; + + for (let i = 0; i < (definition.numberPrecision || 0) && i < 10; i++) { + step /= 10; } - attributeValues = attributeValues.map(attribute => { return { value: attribute }; }); + $input.prop("step", step); + } + else if (definition.labelType === 'boolean') { + $input.prop("type", "checkbox"); - $input.autocomplete({ - appendTo: document.querySelector('body'), - hint: false, - autoselect: false, - openOnFocus: true, - minLength: 0, - tabAutocomplete: false - }, [{ - displayKey: 'value', - source: function (term, cb) { - term = term.toLowerCase(); + if (valueAttr.value === "true") { + $input.prop("checked", "checked"); + } + } + else if (definition.labelType === 'date') { + $input.prop("type", "date"); + } + else if (definition.labelType === 'url') { + $input.prop("placeholder", "http://website..."); - const filtered = attributeValues.filter(attr => attr.value.toLowerCase().includes(term)); + const $openButton = $("") + .addClass("input-group-text open-external-link-button jam jam-arrow-up-right") + .prop("title", "Open external link") + .click(() => window.open($input.val(), '_blank')); - cb(filtered); - } - }]); + $input.after($("
") + .addClass("input-group-append") + .append($openButton)); + } + else { + messagingService.logError("Unknown labelType=" + definitionAttr.labelType); + } + } + else if (valueAttr.type === 'relation') { + if (valueAttr.value) { + $input.val(await treeUtils.getNoteTitle(valueAttr.value)); + } + + // no need to wait for this + noteAutocompleteService.initNoteAutocomplete($input); + + $input.on('autocomplete:selected', (event, suggestion, dataset) => { + this.promotedAttributeChanged(event); }); - } - else if (definition.labelType === 'number') { - $input.prop("type", "number"); - let step = 1; - - for (let i = 0; i < (definition.numberPrecision || 0) && i < 10; i++) { - step /= 10; - } - - $input.prop("step", step); - } - else if (definition.labelType === 'boolean') { - $input.prop("type", "checkbox"); - - if (valueAttr.value === "true") { - $input.prop("checked", "checked"); - } - } - else if (definition.labelType === 'date') { - $input.prop("type", "date"); - } - else if (definition.labelType === 'url') { - $input.prop("placeholder", "http://website..."); - - const $openButton = $("") - .addClass("input-group-text open-external-link-button jam jam-arrow-up-right") - .prop("title", "Open external link") - .click(() => window.open($input.val(), '_blank')); - - $input.after($("
") - .addClass("input-group-append") - .append($openButton)); + $input.setSelectedPath(valueAttr.value); } else { - messagingService.logError("Unknown labelType=" + definitionAttr.labelType); - } - } - else if (valueAttr.type === 'relation') { - if (valueAttr.value) { - $input.val(await treeUtils.getNoteTitle(valueAttr.value)); + messagingService.logError("Unknown attribute type=" + valueAttr.type); + return; } - // no need to wait for this - noteAutocompleteService.initNoteAutocomplete($input); + if (definition.multiplicityType === "multivalue") { + const addButton = $("") + .addClass("jam jam-plus pointer") + .prop("title", "Add new attribute") + .click(async () => { + const $new = await this.createPromotedAttributeRow(definitionAttr, { + attributeId: "", + type: valueAttr.type, + name: definitionAttr.name, + value: "" + }); - $input.on('autocomplete:selected', function(event, suggestion, dataset) { - promotedAttributeChanged(event); - }); + $tr.after($new); - $input.setSelectedPath(valueAttr.value); - } - else { - messagingService.logError("Unknown attribute type=" + valueAttr.type); - return; - } - - if (definition.multiplicityType === "multivalue") { - const addButton = $("") - .addClass("jam jam-plus pointer") - .prop("title", "Add new attribute") - .click(async () => { - const $new = await createPromotedAttributeRow(definitionAttr, { - attributeId: "", - type: valueAttr.type, - name: definitionAttr.name, - value: "" + $new.find('input').focus(); }); - $tr.after($new); + const removeButton = $("") + .addClass("jam jam-trash-alt pointer") + .prop("title", "Remove this attribute") + .click(async () => { + if (valueAttr.attributeId) { + await server.remove("notes/" + noteId + "/attributes/" + valueAttr.attributeId); + } - $new.find('input').focus(); - }); + $tr.remove(); + }); - const removeButton = $("") - .addClass("jam jam-trash-alt pointer") - .prop("title", "Remove this attribute") - .click(async () => { - if (valueAttr.attributeId) { - await server.remove("notes/" + noteId + "/attributes/" + valueAttr.attributeId); - } + $multiplicityCell.append(addButton).append("  ").append(removeButton); + } - $tr.remove(); - }); - - $multiplicityCell.append(addButton).append("  ").append(removeButton); + return $tr; } - return $tr; -} + async promotedAttributeChanged(event) { + const $attr = $(event.target); -async function promotedAttributeChanged(event) { - const $attr = $(event.target); + let value; - let value; + if ($attr.prop("type") === "checkbox") { + value = $attr.is(':checked') ? "true" : "false"; + } + else if ($attr.prop("attribute-type") === "relation") { + const selectedPath = $attr.getSelectedPath(); - if ($attr.prop("type") === "checkbox") { - value = $attr.is(':checked') ? "true" : "false"; - } - else if ($attr.prop("attribute-type") === "relation") { - const selectedPath = $attr.getSelectedPath(); + value = selectedPath ? treeUtils.getNoteIdFromNotePath(selectedPath) : ""; + } + else { + value = $attr.val(); + } - value = selectedPath ? treeUtils.getNoteIdFromNotePath(selectedPath) : ""; - } - else { - value = $attr.val(); - } + const result = await server.put(`notes/${this.ctx.note.noteId}/attribute`, { + attributeId: $attr.prop("attribute-id"), + type: $attr.prop("attribute-type"), + name: $attr.prop("attribute-name"), + value: value + }); - const result = await server.put("notes/" + noteDetailService.getActiveNoteId() + "/attribute", { - attributeId: $attr.prop("attribute-id"), - type: $attr.prop("attribute-type"), - name: $attr.prop("attribute-name"), - value: value - }); + $attr.prop("attribute-id", result.attributeId); - $attr.prop("attribute-id", result.attributeId); - - // animate only if it's not being animated already, this is important especially for e.g. number inputs - // which can be changed many times in a second by clicking on higher/lower buttons. - if ($savedIndicator.queue().length === 0) { - $savedIndicator.fadeOut(); - $savedIndicator.fadeIn(); + // animate only if it's not being animated already, this is important especially for e.g. number inputs + // which can be changed many times in a second by clicking on higher/lower buttons. + if (this.$savedIndicator.queue().length === 0) { + this.$savedIndicator.fadeOut(); + this.$savedIndicator.fadeIn(); + } } } -export default { - getAttributes, - showAttributes, - refreshAttributes, - invalidateAttributes -} \ No newline at end of file +export default Attributes; \ No newline at end of file diff --git a/src/public/javascripts/services/note_context.js b/src/public/javascripts/services/note_context.js index 3f3f536a3..718e8273a 100644 --- a/src/public/javascripts/services/note_context.js +++ b/src/public/javascripts/services/note_context.js @@ -2,9 +2,11 @@ import treeService from "./tree.js"; import protectedSessionHolder from "./protected_session_holder.js"; import server from "./server.js"; import bundleService from "./bundle.js"; -import attributeService from "./attributes.js"; +import Attributes from "./attributes.js"; import treeUtils from "./tree_utils.js"; import utils from "./utils.js"; +import {NoteTypeContext} from "./note_type.js"; +import noteDetailService from "./note_detail.js"; import noteDetailCode from "./note_detail_code.js"; import noteDetailText from "./note_detail_text.js"; import noteDetailFile from "./note_detail_file.js"; @@ -44,6 +46,8 @@ class NoteContext { this.$savedIndicator = this.$noteTabContent.find(".saved-indicator"); this.noteChangeDisabled = false; this.isNoteChanged = false; + this.attributes = new Attributes(this); + this.noteType = new NoteTypeContext(this); this.components = {}; this.$noteTitle.on('input', () => { @@ -70,6 +74,8 @@ class NoteContext { this.$noteTabContent.attr('data-note-id', note.noteId); chromeTabs.updateTab(this.tab, {title: note.title}); + + this.attributes.invalidateAttributes(); } getComponent(type) { @@ -90,7 +96,7 @@ class NoteContext { } this.note.title = this.$noteTitle.val(); - this.note.content = getActiveNoteContent(this.note); + this.note.content = noteDetailService.getActiveNoteContent(); // it's important to set the flag back to false immediatelly after retrieving title and content // otherwise we might overwrite another change (especially async code) @@ -127,9 +133,7 @@ class NoteContext { } async showChildrenOverview() { - return; // FIXME - - const attributes = await attributeService.getAttributes(); + const attributes = await this.attributes.getAttributes(); const hideChildrenOverview = attributes.some(attr => attr.type === 'label' && attr.name === 'hideChildrenOverview') || this.note.type === 'relation-map' || this.note.type === 'image' diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index 1f70aee96..161241bbd 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -9,7 +9,6 @@ import infoService from "./info.js"; import treeCache from "./tree_cache.js"; import NoteFull from "../entities/note_full.js"; import bundleService from "./bundle.js"; -import attributeService from "./attributes.js"; import utils from "./utils.js"; import importDialog from "../dialogs/import.js"; @@ -167,8 +166,8 @@ async function loadNoteDetail(noteId, newTab = false) { ctx.$noteTitle.val(ctx.note.title); if (utils.isDesktop()) { - noteTypeService.setNoteType(ctx.note.type); - noteTypeService.setNoteMime(ctx.note.mime); + ctx.noteType.type(ctx.note.type); + ctx.noteType.mime(ctx.note.mime); } for (const componentType in ctx.components) { @@ -204,11 +203,11 @@ async function loadNoteDetail(noteId, newTab = false) { await bundleService.executeRelationBundles(ctx.note, 'runOnNoteView'); - // if (utils.isDesktop()) { - // await attributeService.showAttributes(); - // - // await ctx.showChildrenOverview(); - // } + if (utils.isDesktop()) { + await ctx.attributes.showAttributes(); + + await ctx.showChildrenOverview(); + } } async function loadNote(noteId) { @@ -293,5 +292,6 @@ export default { focusAndSelectTitle, saveNotesIfChanged, onNoteChange, - addDetailLoadedListener + addDetailLoadedListener, + getActiveContext }; \ No newline at end of file diff --git a/src/public/javascripts/services/note_detail_image.js b/src/public/javascripts/services/note_detail_image.js index 027410afa..315d64baa 100644 --- a/src/public/javascripts/services/note_detail_image.js +++ b/src/public/javascripts/services/note_detail_image.js @@ -8,6 +8,7 @@ class NoteDetailImage { * @param {NoteContext} ctx */ constructor(ctx) { + this.ctx = ctx; this.$component = ctx.$noteTabContent.find('.note-detail-image'); this.$imageWrapper = ctx.$noteTabContent.find('.note-detail-image-wrapper'); this.$imageView = ctx.$noteTabContent.find('.note-detail-image-view'); @@ -42,18 +43,16 @@ class NoteDetailImage { } async show() { - const activeNote = noteDetailService.getActiveNote(); - - const attributes = await server.get('notes/' + activeNote.noteId + '/attributes'); + const attributes = await server.get('notes/' + this.ctx.note.noteId + '/attributes'); const attributeMap = utils.toObject(attributes, l => [l.name, l.value]); this.$component.show(); this.$fileName.text(attributeMap.originalFileName || "?"); this.$fileSize.text((attributeMap.fileSize || "?") + " bytes"); - this.$fileType.text(activeNote.mime); + this.$fileType.text(this.ctx.note.mime); - this.$imageView.prop("src", `api/images/${activeNote.noteId}/${activeNote.title}`); + this.$imageView.prop("src", `api/images/${this.ctx.note.noteId}/${this.ctx.note.title}`); } selectImage(element) { diff --git a/src/public/javascripts/services/note_detail_text.js b/src/public/javascripts/services/note_detail_text.js index 1aba4c3e7..e235c99b1 100644 --- a/src/public/javascripts/services/note_detail_text.js +++ b/src/public/javascripts/services/note_detail_text.js @@ -1,7 +1,5 @@ import libraryLoader from "./library_loader.js"; -import noteDetailService from './note_detail.js'; import treeService from './tree.js'; -import attributeService from "./attributes.js"; class NoteDetailText { /** @@ -69,7 +67,7 @@ class NoteDetailText { } async isReadOnly() { - const attributes = await attributeService.getAttributes(); + const attributes = await this.ctx.attributes.getAttributes(); return attributes.some(attr => attr.type === 'label' && attr.name === 'readOnly'); } diff --git a/src/public/javascripts/services/note_type.js b/src/public/javascripts/services/note_type.js index b54ba3436..f6e5ffb0d 100644 --- a/src/public/javascripts/services/note_type.js +++ b/src/public/javascripts/services/note_type.js @@ -4,10 +4,6 @@ import server from './server.js'; import infoService from "./info.js"; import confirmDialog from "../dialogs/confirm.js"; -const $executeScriptButton = $("#execute-script-button"); -const $toggleEditButton = $('#toggle-edit-button'); -const $renderButton = $('#render-button'); - const DEFAULT_MIME_TYPES = [ { mime: 'text/x-csrc', title: 'C' }, { mime: 'text/x-c++src', title: 'C++' }, @@ -45,15 +41,24 @@ const DEFAULT_MIME_TYPES = [ { mime: 'text/x-yaml', title: 'YAML' } ]; -let noteTypeModel; +let mimeTypes = DEFAULT_MIME_TYPES; -function NoteTypeModel() { +/** + * @param {NoteContext} ctx + * @constructor + */ +function NoteTypeContext(ctx) { const self = this; + this.$executeScriptButton = ctx.$noteTabContent.find(".execute-script-button"); + this.$toggleEditButton = ctx.$noteTabContent.find('.toggle-edit-button'); + this.$renderButton = ctx.$noteTabContent.find('.render-button'); + + this.ctx = ctx; this.type = ko.observable('text'); this.mime = ko.observable(''); - this.codeMimeTypes = ko.observableArray(DEFAULT_MIME_TYPES); + this.codeMimeTypes = ko.observableArray(mimeTypes); this.typeString = function() { const type = self.type(); @@ -97,9 +102,7 @@ function NoteTypeModel() { }; async function save() { - const note = noteDetailService.getActiveNote(); - - await server.put('notes/' + note.noteId + await server.put('notes/' + self.ctx.note.noteId + '/type/' + encodeURIComponent(self.type()) + '/mime/' + encodeURIComponent(self.mime())); @@ -175,32 +178,21 @@ function NoteTypeModel() { }; this.updateExecuteScriptButtonVisibility = function() { - $executeScriptButton.toggle(self.mime().startsWith('application/javascript')); + self.$executeScriptButton.toggle(self.mime().startsWith('application/javascript')); - $toggleEditButton.toggle(self.type() === 'render'); - $renderButton.toggle(self.type() === 'render'); - } -} + self.$toggleEditButton.toggle(self.type() === 'render'); + self.$renderButton.toggle(self.type() === 'render'); + }; -function init() { - noteTypeModel = new NoteTypeModel(); - - ko.applyBindings(noteTypeModel, document.getElementById('note-type-wrapper')); + ko.applyBindings(this, ctx.$noteTabContent.find('.note-type-wrapper')[0]) } export default { - getNoteType: () => noteTypeModel.type(), - setNoteType: type => noteTypeModel.type(type), - - getNoteMime: () => noteTypeModel.mime(), - setNoteMime: mime => { - noteTypeModel.mime(mime); - - noteTypeModel.updateExecuteScriptButtonVisibility(); - }, - getDefaultCodeMimeTypes: () => DEFAULT_MIME_TYPES.slice(), - getCodeMimeTypes: () => noteTypeModel.codeMimeTypes(), - setCodeMimeTypes: types => noteTypeModel.codeMimeTypes(types), - init + getCodeMimeTypes: () => mimeTypes, + setCodeMimeTypes: types => { mimeTypes = types; } +}; + +export { + NoteTypeContext }; \ No newline at end of file diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index 1895e8d3e..aefcc7e69 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -412,7 +412,7 @@ div.ui-tooltip { font-size: larger; } -#children-overview { +.children-overview { flex-grow: 1000; flex-shrink: 1000; flex-basis: 0;