|
|
|
|
|
|
|
|
|
const toolbar = { |
|
_locked: false, |
|
_toolbar: document.getElementById("ui-toolbar"), |
|
_toolbar_lock_indicator: document.getElementById("toolbar-lock-indicator"), |
|
|
|
tools: [], |
|
_current_tool: null, |
|
get currentTool() { |
|
return this._current_tool; |
|
}, |
|
|
|
lock() { |
|
toolbar._locked = true; |
|
toolbar._toolbar_lock_indicator.style.display = "block"; |
|
}, |
|
unlock() { |
|
toolbar._locked = false; |
|
toolbar._toolbar_lock_indicator.style.display = "none"; |
|
}, |
|
|
|
_makeToolbarEntry: (tool) => { |
|
const toolTitle = document.createElement("img"); |
|
toolTitle.classList.add("tool-icon"); |
|
toolTitle.src = tool.icon; |
|
|
|
const toolEl = document.createElement("div"); |
|
toolEl.id = `tool-${tool.id}`; |
|
toolEl.classList.add("tool"); |
|
toolEl.title = tool.name; |
|
if (tool.options.shortcut) toolEl.title += ` (${tool.options.shortcut})`; |
|
toolEl.onclick = () => tool.enable(); |
|
|
|
toolEl.appendChild(toolTitle); |
|
|
|
return toolEl; |
|
}, |
|
|
|
registerTool( |
|
icon, |
|
toolname, |
|
enable, |
|
disable, |
|
options = { |
|
/** |
|
* Runs on tool creation. It receives the tool state. |
|
* |
|
* Can be used to setup callback functions, for example. |
|
*/ |
|
init: null, |
|
/** |
|
* Function to populate the state menu. |
|
* |
|
* It receives a div element (that is the menu) and the current tool state. |
|
*/ |
|
populateContextMenu: null, |
|
/** |
|
* Help description of the tool; for now used for nothing |
|
*/ |
|
description: "", |
|
/** |
|
* Shortcut; Text describing this tool's shortcut access |
|
*/ |
|
shortcut: "", |
|
} |
|
) { |
|
// Set some defaults |
|
if (!options.init) |
|
options.init = (state) => console.debug(`Initialized tool '${toolname}'`); |
|
|
|
if (!options.populateContextMenu) |
|
options.populateContextMenu = (menu, state) => { |
|
const span = document.createElement("span"); |
|
span.textContent = "Tool has no context menu"; |
|
menu.appendChild(span); |
|
return; |
|
}; |
|
|
|
// Create tool |
|
const id = guid(); |
|
|
|
const contextMenuEl = document.getElementById("tool-context"); |
|
|
|
const tool = { |
|
id, |
|
icon, |
|
name: toolname, |
|
enabled: false, |
|
_element: null, |
|
state: { |
|
redrawui: () => tool.state.redraw && tool.state.redraw(), |
|
}, |
|
options, |
|
/** |
|
* If the tool has a redraw() function in its state, then run it |
|
*/ |
|
redraw: () => { |
|
tool.state.redrawui && tool.state.redrawui(); |
|
tool.state.redraw && tool.state.redraw(); |
|
}, |
|
redrawui: () => { |
|
tool.state.redrawui && tool.state.redrawui(); |
|
}, |
|
enable: (opt = null) => { |
|
if (toolbar._locked) return; |
|
|
|
this.tools.filter((t) => t.enabled).forEach((t) => t.disable()); |
|
|
|
while (contextMenuEl.lastChild) { |
|
contextMenuEl.removeChild(contextMenuEl.lastChild); |
|
} |
|
options.populateContextMenu(contextMenuEl, tool.state, tool); |
|
|
|
tool._element && tool._element.classList.add("using"); |
|
tool.enabled = true; |
|
|
|
this._current_tool = tool; |
|
enable(tool.state, opt, tool); |
|
}, |
|
disable: (opt = null) => { |
|
tool._element && tool._element.classList.remove("using"); |
|
this._current_tool = null; |
|
tool.enabled = false; |
|
disable(tool.state, opt, tool); |
|
}, |
|
}; |
|
|
|
// Initalize |
|
options.init && options.init(tool.state, tool); |
|
|
|
this.tools.push(tool); |
|
|
|
// Add tool to toolbar |
|
tool._element = this._makeToolbarEntry(tool); |
|
this._toolbar.appendChild(tool._element); |
|
|
|
return tool; |
|
}, |
|
|
|
addSeparator() { |
|
const separator = document.createElement("div"); |
|
separator.classList.add("separator"); |
|
this._toolbar.appendChild(separator); |
|
}, |
|
}; |
|
|
|
/** |
|
* Premade inputs for populating the context menus |
|
*/ |
|
const _toolbar_input = { |
|
checkbox: (state, lsKey, dataKey, text, classes, cb = null) => { |
|
if (state[dataKey] === undefined) state[dataKey] = false; |
|
|
|
const savedValueStr = lsKey && localStorage.getItem(lsKey); |
|
const savedValue = savedValueStr && JSON.parse(savedValueStr); |
|
|
|
const checkbox = document.createElement("input"); |
|
checkbox.type = "checkbox"; |
|
checkbox.classList.add("oo-checkbox", "ui", "inline-icon"); |
|
|
|
if (savedValue !== null) state[dataKey] = checkbox.checked = savedValue; |
|
|
|
if (typeof classes === "string") classes = [classes]; |
|
|
|
if (classes) checkbox.classList.add(...classes); |
|
checkbox.checked = state[dataKey]; |
|
checkbox.onchange = () => { |
|
if (lsKey) localStorage.setItem(lsKey, JSON.stringify(checkbox.checked)); |
|
state[dataKey] = checkbox.checked; |
|
cb && cb(); |
|
}; |
|
|
|
checkbox.title = text; |
|
|
|
const label = document.createElement("label"); |
|
label.appendChild(checkbox); |
|
label.appendChild(new Text(text)); |
|
|
|
return { |
|
checkbox, |
|
label, |
|
setValue(v) { |
|
checkbox.checked = v; |
|
state[dataKey] = checkbox.checked; |
|
cb && cb(); |
|
return checkbox.checked; |
|
}, |
|
}; |
|
}, |
|
|
|
slider: (state, lsKey, dataKey, text, options = {}) => { |
|
defaultOpt(options, {min: 0, max: 1, step: 0.1, textStep: null, cb: null}); |
|
const slider = document.createElement("div"); |
|
|
|
const savedValueStr = lsKey && localStorage.getItem(lsKey); |
|
const savedValue = savedValueStr && JSON.parse(savedValueStr); |
|
|
|
const value = createSlider(text, slider, { |
|
min: options.min, |
|
max: options.max, |
|
step: options.step, |
|
valuecb: (v) => { |
|
if (lsKey) localStorage.setItem(lsKey, JSON.stringify(v)); |
|
state[dataKey] = v; |
|
options.cb && options.cb(v); |
|
}, |
|
defaultValue: state[dataKey], |
|
textStep: options.textStep, |
|
}); |
|
|
|
if (savedValue !== null) value.value = savedValue; |
|
|
|
return { |
|
slider, |
|
rawSlider: value, |
|
setValue(v) { |
|
value.value = v; |
|
return value.value; |
|
}, |
|
}; |
|
}, |
|
|
|
selectlist: ( |
|
state, |
|
lsKey, |
|
dataKey, |
|
text, |
|
options = {value, text}, |
|
defaultOptionValue, |
|
cb = null |
|
) => { |
|
const savedValueStr = lsKey && localStorage.getItem(lsKey); |
|
const savedValue = savedValueStr && JSON.parse(savedValueStr); |
|
|
|
const selectlist = document.createElement("select"); |
|
selectlist.classList.add("bareselector"); |
|
Object.entries(options).forEach((opt) => { |
|
var option = document.createElement("option"); |
|
option.value = opt[0]; |
|
option.text = opt[1]; |
|
selectlist.options.add(option); |
|
}); |
|
selectlist.selectedIndex = defaultOptionValue; |
|
|
|
if (savedValue !== null) |
|
state[dataKey] = selectlist.selectedIndex = savedValue; |
|
|
|
selectlist.onchange = () => { |
|
if (lsKey) |
|
localStorage.setItem(lsKey, JSON.stringify(selectlist.selectedIndex)); |
|
state[dataKey] = selectlist.selectedIndex; |
|
cb && cb(); |
|
}; |
|
const label = document.createElement("label"); |
|
label.appendChild(new Text(text)); |
|
label.appendChild(selectlist); |
|
return {selectlist, label}; |
|
}, |
|
}; |
|
|