2018-01-11 13:01:16 +08:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const attributesDialog = (function() {
|
|
|
|
const dialogEl = $("#attributes-dialog");
|
2018-02-05 12:16:45 +08:00
|
|
|
const saveAttributesButton = $("#save-attributes-button");
|
2018-01-12 10:40:09 +08:00
|
|
|
const attributesModel = new AttributesModel();
|
2018-02-05 08:27:27 +08:00
|
|
|
let attributeNames = [];
|
2018-01-11 13:01:16 +08:00
|
|
|
|
2018-01-12 10:40:09 +08:00
|
|
|
function AttributesModel() {
|
|
|
|
const self = this;
|
2018-01-11 13:01:16 +08:00
|
|
|
|
2018-01-12 10:40:09 +08:00
|
|
|
this.attributes = ko.observableArray();
|
|
|
|
|
|
|
|
this.loadAttributes = async function() {
|
|
|
|
const noteId = noteEditor.getCurrentNoteId();
|
|
|
|
|
|
|
|
const attributes = await server.get('notes/' + noteId + '/attributes');
|
|
|
|
|
2018-02-05 06:22:21 +08:00
|
|
|
self.attributes(attributes.map(ko.observable));
|
2018-01-11 13:01:16 +08:00
|
|
|
|
2018-02-05 06:22:21 +08:00
|
|
|
addLastEmptyRow();
|
2018-02-05 08:27:27 +08:00
|
|
|
|
|
|
|
attributeNames = await server.get('attributes/names');
|
|
|
|
|
2018-02-06 10:07:18 +08:00
|
|
|
// attribute might not be rendered immediatelly so could not focus
|
|
|
|
setTimeout(() => $(".attribute-name:last").focus(), 100);
|
2018-01-12 10:40:09 +08:00
|
|
|
};
|
|
|
|
|
2018-02-05 06:22:21 +08:00
|
|
|
function isValid() {
|
|
|
|
for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) {
|
2018-02-07 10:18:09 +08:00
|
|
|
if (self.isEmptyName(i)) {
|
2018-02-05 06:22:21 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-01-12 10:40:09 +08:00
|
|
|
this.save = async function() {
|
2018-02-05 12:16:45 +08:00
|
|
|
// we need to defocus from input (in case of enter-triggered save) because value is updated
|
|
|
|
// on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would
|
|
|
|
// stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel.
|
|
|
|
saveAttributesButton.focus();
|
|
|
|
|
2018-02-05 06:22:21 +08:00
|
|
|
if (!isValid()) {
|
|
|
|
alert("Please fix all validation errors and try saving again.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-12 10:40:09 +08:00
|
|
|
const noteId = noteEditor.getCurrentNoteId();
|
|
|
|
|
2018-02-05 06:22:21 +08:00
|
|
|
const attributesToSave = self.attributes()
|
|
|
|
.map(attr => attr())
|
|
|
|
.filter(attr => attr.attributeId !== "" || attr.name !== "");
|
|
|
|
|
|
|
|
const attributes = await server.put('notes/' + noteId + '/attributes', attributesToSave);
|
|
|
|
|
|
|
|
self.attributes(attributes.map(ko.observable));
|
2018-01-12 10:40:09 +08:00
|
|
|
|
2018-02-05 06:22:21 +08:00
|
|
|
addLastEmptyRow();
|
2018-01-12 10:40:09 +08:00
|
|
|
|
|
|
|
showMessage("Attributes have been saved.");
|
2018-02-05 09:23:30 +08:00
|
|
|
|
|
|
|
noteEditor.loadAttributeList();
|
2018-01-12 10:40:09 +08:00
|
|
|
};
|
2018-02-05 06:22:21 +08:00
|
|
|
|
|
|
|
function addLastEmptyRow() {
|
|
|
|
const attrs = self.attributes();
|
2018-02-06 10:07:18 +08:00
|
|
|
const last = attrs.length === 0 ? null : attrs[attrs.length - 1]();
|
2018-02-05 06:22:21 +08:00
|
|
|
|
2018-02-06 10:07:18 +08:00
|
|
|
if (!last || last.name.trim() !== "" || last.value !== "") {
|
2018-02-05 06:22:21 +08:00
|
|
|
self.attributes.push(ko.observable({
|
|
|
|
attributeId: '',
|
|
|
|
name: '',
|
|
|
|
value: ''
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.attributeChanged = function (row) {
|
|
|
|
addLastEmptyRow();
|
|
|
|
|
|
|
|
for (const attr of self.attributes()) {
|
|
|
|
if (row.attributeId === attr().attributeId) {
|
|
|
|
attr.valueHasMutated();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
this.isNotUnique = function(index) {
|
|
|
|
const cur = self.attributes()[index]();
|
|
|
|
|
|
|
|
if (cur.name.trim() === "") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) {
|
|
|
|
const attr = attrs[i]();
|
|
|
|
|
|
|
|
if (index !== i && cur.name === attr.name) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
this.isEmptyName = function(index) {
|
|
|
|
const cur = self.attributes()[index]();
|
|
|
|
|
|
|
|
return cur.name.trim() === "" && (cur.attributeId !== "" || cur.value !== "");
|
|
|
|
}
|
2018-01-11 13:01:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async function showDialog() {
|
|
|
|
glob.activeDialog = dialogEl;
|
|
|
|
|
2018-02-05 08:43:11 +08:00
|
|
|
await attributesModel.loadAttributes();
|
|
|
|
|
2018-01-11 13:01:16 +08:00
|
|
|
dialogEl.dialog({
|
|
|
|
modal: true,
|
|
|
|
width: 800,
|
2018-02-04 01:44:22 +08:00
|
|
|
height: 500
|
2018-01-11 13:01:16 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
$(document).bind('keydown', 'alt+a', e => {
|
|
|
|
showDialog();
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
|
2018-01-22 12:06:25 +08:00
|
|
|
ko.applyBindings(attributesModel, document.getElementById('attributes-dialog'));
|
2018-01-12 10:40:09 +08:00
|
|
|
|
2018-02-05 08:43:11 +08:00
|
|
|
$(document).on('focus', '.attribute-name', function (e) {
|
|
|
|
if (!$(this).hasClass("ui-autocomplete-input")) {
|
|
|
|
$(this).autocomplete({
|
|
|
|
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
|
|
|
// because we have overriden filter() function in init.js
|
|
|
|
source: attributeNames.map(attr => {
|
|
|
|
return {
|
|
|
|
label: attr,
|
|
|
|
value: attr
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
minLength: 0
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
$(this).autocomplete("search", $(this).val());
|
|
|
|
});
|
|
|
|
|
|
|
|
$(document).on('focus', '.attribute-value', async function (e) {
|
|
|
|
if (!$(this).hasClass("ui-autocomplete-input")) {
|
|
|
|
const attributeName = $(this).parent().parent().find('.attribute-name').val();
|
|
|
|
|
|
|
|
if (attributeName.trim() === "") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const attributeValues = await server.get('attributes/values/' + encodeURIComponent(attributeName));
|
|
|
|
|
|
|
|
if (attributeValues.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$(this).autocomplete({
|
|
|
|
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
|
|
|
// because we have overriden filter() function in init.js
|
|
|
|
source: attributeValues.map(attr => {
|
|
|
|
return {
|
|
|
|
label: attr,
|
|
|
|
value: attr
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
minLength: 0
|
|
|
|
});
|
|
|
|
}
|
2018-02-05 08:27:27 +08:00
|
|
|
|
|
|
|
$(this).autocomplete("search", $(this).val());
|
|
|
|
});
|
|
|
|
|
2018-01-11 13:01:16 +08:00
|
|
|
return {
|
|
|
|
showDialog
|
|
|
|
};
|
|
|
|
})();
|