244 lines
6.1 KiB
JavaScript
244 lines
6.1 KiB
JavaScript
module.exports = Interface;
|
|
|
|
var m = require("mithril");
|
|
|
|
/**
|
|
* Options main interface
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @param object settings Settings object
|
|
* @param function saveSettings Function to save settings
|
|
* @return void
|
|
*/
|
|
function Interface(settings, saveSettings) {
|
|
// public methods
|
|
this.attach = attach;
|
|
this.view = view;
|
|
|
|
// fields
|
|
this.settings = settings;
|
|
this.saveSettings = saveSettings;
|
|
this.saveEnabled = false;
|
|
|
|
// 'default' store must not be displayed or later attempted to be saved
|
|
delete this.settings.stores.default;
|
|
}
|
|
|
|
/**
|
|
* Attach the interface on the given element
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @param DOMElement element Target element
|
|
* @return void
|
|
*/
|
|
function attach(element) {
|
|
m.mount(element, this);
|
|
}
|
|
|
|
/**
|
|
* Generates vnodes for render
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @param function ctl Controller
|
|
* @param object params Runtime params
|
|
* @return []Vnode
|
|
*/
|
|
function view(ctl, params) {
|
|
var nodes = [];
|
|
nodes.push(m("h3", "Basic settings"));
|
|
nodes.push(createCheckbox.call(this, "autoSubmit", "Automatically submit forms after filling"));
|
|
nodes.push(createInput.call(this, "username", "Default username", "john.smith"));
|
|
nodes.push(createInput.call(this, "gpgPath", "Custom gpg binary", "/path/to/gpg"));
|
|
|
|
nodes.push(m("h3", "Custom store locations"));
|
|
nodes.push(
|
|
m("div", { class: "notice" }, "(this overrides default store and $PASSWORD_STORE_DIR)")
|
|
);
|
|
for (var storeId in this.settings.stores) {
|
|
nodes.push(createCustomStore.call(this, storeId));
|
|
}
|
|
nodes.push(
|
|
m(
|
|
"a.add-store",
|
|
{
|
|
onclick: () => {
|
|
addEmptyStore(this.settings.stores);
|
|
this.saveEnabled = true;
|
|
}
|
|
},
|
|
"Add store"
|
|
)
|
|
);
|
|
|
|
if (typeof this.error !== "undefined") {
|
|
nodes.push(m("div.error", this.error.message));
|
|
}
|
|
if (this.settings.hasOwnProperty("hostError")) {
|
|
let hostError = this.settings.hostError;
|
|
nodes.push(m("div.error", hostError.params.message));
|
|
}
|
|
|
|
nodes.push(
|
|
m(
|
|
"button.save",
|
|
{
|
|
disabled: !this.saveEnabled,
|
|
onclick: async () => {
|
|
try {
|
|
this.settings = await this.saveSettings(this.settings);
|
|
this.error = undefined;
|
|
} catch (e) {
|
|
this.error = e;
|
|
}
|
|
this.saveEnabled = false;
|
|
m.redraw();
|
|
}
|
|
},
|
|
"Save"
|
|
)
|
|
);
|
|
return nodes;
|
|
}
|
|
|
|
/**
|
|
* Generates vnode for a input setting
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @param string key Settings key
|
|
* @param string title Settings title
|
|
* @param string placeholder Settings placeholder
|
|
* @return Vnode
|
|
*/
|
|
function createInput(key, title, placeholder) {
|
|
return m("div.option", { class: key }, [
|
|
m("label", [
|
|
title,
|
|
m("input[type=text]", {
|
|
value: this.settings[key],
|
|
placeholder: placeholder,
|
|
onchange: e => {
|
|
this.settings[key] = e.target.value;
|
|
this.saveEnabled = true;
|
|
}
|
|
})
|
|
])
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Generates vnode for a checkbox setting
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @param string key Settings key
|
|
* @param string title Label for the checkbox
|
|
* @return Vnode
|
|
*/
|
|
function createCheckbox(key, title) {
|
|
return m("div.option", { class: key }, [
|
|
m("label", [
|
|
m("input[type=checkbox]", {
|
|
title: title,
|
|
checked: this.settings[key],
|
|
onchange: e => {
|
|
this.settings[key] = e.target.checked;
|
|
this.saveEnabled = true;
|
|
}
|
|
}),
|
|
title
|
|
])
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Generates vnode for a custom store configuration
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @param string storeId Store ID
|
|
* @return Vnode
|
|
*/
|
|
function createCustomStore(storeId) {
|
|
let store = this.settings.stores[storeId];
|
|
|
|
return m("div.option.custom-store", { class: "store-" + store.name }, [
|
|
m("input[type=text].name", {
|
|
title: "The name for this password store",
|
|
value: store.name,
|
|
placeholder: "name",
|
|
onchange: e => {
|
|
store.name = e.target.value;
|
|
this.saveEnabled = true;
|
|
}
|
|
}),
|
|
m("input[type=text].path", {
|
|
title: "The full path to this password store",
|
|
value: store.path,
|
|
placeholder: "/path/to/store",
|
|
onchange: e => {
|
|
store.path = e.target.value;
|
|
this.saveEnabled = true;
|
|
}
|
|
}),
|
|
m("input[type=text].bgColor", {
|
|
title: "Badge background color",
|
|
value: store.bgColor,
|
|
placeholder: "#626262",
|
|
onchange: e => {
|
|
store.bgColor = e.target.value;
|
|
this.saveEnabled = true;
|
|
}
|
|
}),
|
|
m("input[type=text].color", {
|
|
title: "Badge text color",
|
|
value: store.color,
|
|
placeholder: "#c4c4c4",
|
|
onchange: e => {
|
|
store.color = e.target.value;
|
|
this.saveEnabled = true;
|
|
}
|
|
}),
|
|
m(
|
|
"a.remove",
|
|
{
|
|
title: "Remove this password store",
|
|
onclick: () => {
|
|
delete this.settings.stores[storeId];
|
|
this.saveEnabled = true;
|
|
}
|
|
},
|
|
"[X]"
|
|
)
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Generates new store ID
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @return string new store ID
|
|
*/
|
|
function newId() {
|
|
return Math.random()
|
|
.toString(36)
|
|
.substr(2, 9);
|
|
}
|
|
|
|
/**
|
|
* Generates a new empty store
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @param []object stores List of stores to add a new store to
|
|
* @return void
|
|
*/
|
|
function addEmptyStore(stores) {
|
|
let store = { id: newId(), name: "", path: "" };
|
|
stores[store.id] = store;
|
|
}
|