diff --git a/desktop/sources/scripts/cursor.js b/desktop/sources/scripts/cursor.js index 3368ad1..d8ca270 100644 --- a/desktop/sources/scripts/cursor.js +++ b/desktop/sources/scripts/cursor.js @@ -1,6 +1,6 @@ 'use strict' -function Cursor () { +function Cursor (dotgrid) { this.pos = { x: 0, y: 0 } this.translation = null this.operation = null @@ -96,4 +96,6 @@ function Cursor () { } function isEqual (a, b) { return a.x === b.x && a.y === b.y } + function clamp (v, min, max) { return v < min ? min : v > max ? max : v } + function step (v, s) { return Math.round(v / s) * s } } diff --git a/desktop/sources/scripts/dotgrid.js b/desktop/sources/scripts/dotgrid.js index f551df0..965983d 100644 --- a/desktop/sources/scripts/dotgrid.js +++ b/desktop/sources/scripts/dotgrid.js @@ -1,23 +1,28 @@ 'use strict' -function Dotgrid () { - const defaultTheme = { - background: '#eee', - f_high: '#000', - f_med: '#999', - f_low: '#ccc', - f_inv: '#000', - b_high: '#000', - b_med: '#888', - b_low: '#aaa', - b_inv: '#ffb545' - } +/* global Acels */ +/* global Theme */ +/* global Source */ +/* global History */ +/* global Manager */ +/* global Renderer */ +/* global Tool */ +/* global Interface */ +/* global Picker */ +/* global Cursor */ + +/* global webFrame */ +/* global FileReader */ + +function Dotgrid () { // ISU this.install = function (host) { console.info('Dotgrid', 'Installing..') - this.theme = new Theme(defaultTheme) + + this.acels = new Acels() + this.theme = new Theme() this.history = new History() this.source = new Source(this) @@ -27,47 +32,86 @@ function Dotgrid () { this.interface = new Interface(this) this.picker = new Picker(this) this.cursor = new Cursor(this) - this.listener = new Listener(this) host.appendChild(this.renderer.el) + // Add events + document.addEventListener('mousedown', (e) => { this.cursor.down(e) }, false) + document.addEventListener('mousemove', (e) => { this.cursor.move(e) }, false) + document.addEventListener('contextmenu', (e) => { this.cursor.alt(e) }, false) + document.addEventListener('mouseup', (e) => { this.cursor.up(e) }, false) + document.addEventListener('copy', (e) => { this.copy(e) }, false) + document.addEventListener('cut', (e) => { this.cut(e) }, false) + document.addEventListener('paste', (e) => { this.paste(e) }, false) + window.addEventListener('resize', (e) => { this.onResize() }, false) + window.addEventListener('dragover', (e) => { e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = 'copy' }) + window.addEventListener('drop', this.drag) + + this.acels.set('File', 'New', 'CmdOrCtrl+N', () => { this.source.new() }) + this.acels.set('File', 'Save', 'CmdOrCtrl+S', () => { this.source.save('export.grid', this.commander._input.value, 'text/plain') }) + this.acels.set('File', 'Export Image', 'CmdOrCtrl+E', () => { this.source.download('export.png', this.surface.el.toDataURL('image/png', 1.0), 'image/png') }) + this.acels.set('File', 'Open', 'CmdOrCtrl+O', () => { this.source.open('grid', this.whenOpen) }) + this.acels.set('File', 'Revert', 'CmdOrCtrl+W', () => { this.source.revert() }) + this.acels.set('History', 'Undo', 'CmdOrCtrl+Z', () => { this.history.undo() }) + this.acels.set('History', 'Redo', 'CmdOrCtrl+Shift+Z', () => { this.history.redo() }) + this.acels.set('Stroke', 'Line', 'A', () => { this.tool.cast('line') }) + this.acels.set('Stroke', 'Arc', 'S', () => { this.tool.cast('arc_c') }) + this.acels.set('Stroke', 'Arc Rev', 'D', () => { this.tool.cast('arc_r') }) + this.acels.set('Stroke', 'Bezier', 'F', () => { this.tool.cast('bezier') }) + this.acels.set('Stroke', 'Close', 'Z', () => { this.tool.cast('close') }) + this.acels.set('Stroke', 'Arc(full)', 'T', () => { this.tool.cast('arc_c_full') }) + this.acels.set('Stroke', 'Arc Rev(full)', 'Y', () => { this.tool.cast('arc_r_full') }) + this.acels.set('Stroke', 'Clear Selection', 'Escape', () => { this.tool.clear() }) + this.acels.set('Effect', 'Linecap', 'Q', () => { this.tool.toggle('linecap') }) + this.acels.set('Effect', 'Linejoin', 'W', () => { this.tool.toggle('linejoin') }) + this.acels.set('Effect', 'Mirror', 'E', () => { this.tool.toggle('mirror') }) + this.acels.set('Effect', 'Fill', 'R', () => { this.tool.toggle('fill') }) + this.acels.set('Effect', 'Thicker', '}', () => { this.tool.toggle('thickness', 1) }) + this.acels.set('Effect', 'Thinner', '{', () => { this.tool.toggle('thickness', -1) }) + this.acels.set('Effect', 'Thicker +5', ']', () => { this.tool.toggle('thickness', 5) }) + this.acels.set('Effect', 'Thinner -5', '[', () => { this.tool.toggle('thickness', -5) }) + this.acels.set('Manual', 'Add Point', 'Enter', () => { this.tool.addVertex(this.cursor.pos); this.renderer.update() }) + this.acels.set('Manual', 'Move Up', 'Up', () => { this.cursor.pos.y -= 15; this.renderer.update() }) + this.acels.set('Manual', 'Move Right', 'Right', () => { this.cursor.pos.x += 15; this.renderer.update() }) + this.acels.set('Manual', 'Move Down', 'Down', () => { this.cursor.pos.y += 15; this.renderer.update() }) + this.acels.set('Manual', 'Move Left', 'Left', () => { this.cursor.pos.x -= 15; this.renderer.update() }) + this.acels.set('Manual', 'Remove Point', 'Shift+Backspace', () => { this.tool.removeSegmentsAt(this.cursor.pos) }) + this.acels.set('Manual', 'Remove Segment', 'Backspace', () => { this.tool.removeSegment() }) + this.acels.set('Layers', 'Foreground', 'CmdOrCtrl+1', () => { this.tool.selectLayer(0) }) + this.acels.set('Layers', 'Middleground', 'CmdOrCtrl+2', () => { this.tool.selectLayer(1) }) + this.acels.set('Layers', 'Background', 'CmdOrCtrl+3', () => { this.tool.selectLayer(2) }) + this.acels.set('Layers', 'Merge Layers', 'CmdOrCtrl+M', () => { this.tool.merge() }) + this.acels.set('View', 'Color Picker', 'G', () => { this.picker.start() }) + this.acels.set('View', 'Toggle Grid', 'H', () => { this.renderer.toggle() }) + this.acels.install(window) + this.manager.install() this.interface.install(host) this.theme.install(host, () => { this.update() }) } - this.start = function () { - console.info('Dotgrid', 'Starting..') + this.start = () => { + console.log('Ronin', 'Starting..') + console.info(`${this.acels}`) + this.theme.start() this.tool.start() this.renderer.start() this.interface.start() - // Add events - document.addEventListener('mousedown', function (e) { dotgrid.cursor.down(e) }, false) - document.addEventListener('mousemove', function (e) { dotgrid.cursor.move(e) }, false) - document.addEventListener('contextmenu', function (e) { dotgrid.cursor.alt(e) }, false) - document.addEventListener('mouseup', function (e) { dotgrid.cursor.up(e) }, false) - document.addEventListener('copy', function (e) { dotgrid.copy(e) }, false) - document.addEventListener('cut', function (e) { dotgrid.cut(e) }, false) - document.addEventListener('paste', function (e) { dotgrid.paste(e) }, false) - window.addEventListener('resize', function (e) { dotgrid.onResize() }, false) - window.addEventListener('dragover', function (e) { e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = 'copy' }) - window.addEventListener('drop', dotgrid.drag) - this.source.new() this.onResize() setTimeout(() => { document.body.className += ' ready' }, 250) } - this.update = function () { + this.update = () => { this.manager.update() this.interface.update() this.renderer.update() } - this.clear = function () { + this.clear = () => { this.history.clear() this.tool.reset() this.reset() @@ -75,7 +119,7 @@ function Dotgrid () { this.interface.update(true) } - this.reset = function () { + this.reset = () => { this.tool.clear() this.update() } @@ -103,7 +147,7 @@ function Dotgrid () { // Resize Tools - this.fitSize = function () { + this.fitSize = () => { if (this.requireResize() === false) { return } console.log('Dotgrid', `Will resize to: ${printSize(this.getRequiredSize())}`) this.setWindowSize(this.getRequiredSize()) @@ -118,47 +162,47 @@ function Dotgrid () { this.update() } - this.getPadding = function () { + this.getPadding = () => { return { x: 60, y: 90 } } - this.getWindowSize = function () { + this.getWindowSize = () => { return { width: window.innerWidth, height: window.innerHeight } } - this.getProjectSize = function () { + this.getProjectSize = () => { return this.tool.settings.size } - this.getPaddedSize = function () { + this.getPaddedSize = () => { const rect = this.getWindowSize() const pad = this.getPadding() return { width: step(rect.width - pad.x, 15), height: step(rect.height - pad.y, 15) } } - this.getRequiredSize = function () { + this.getRequiredSize = () => { const rect = this.getProjectSize() const pad = this.getPadding() return { width: step(rect.width, 15) + pad.x, height: step(rect.height, 15) + pad.y } } - this.requireResize = function () { + this.requireResize = () => { const _window = this.getWindowSize() const _required = this.getRequiredSize() const offset = sizeOffset(_window, _required) if (offset.width !== 0 || offset.height !== 0) { - console.log(`Dotgrid`, `Require ${printSize(_required)}, but window is ${printSize(_window)}(${printSize(offset)})`) + console.log('Dotgrid', `Require ${printSize(_required)}, but window is ${printSize(_window)}(${printSize(offset)})`) return true } return false } - this.onResize = function () { + this.onResize = () => { const _project = this.getProjectSize() const _padded = this.getPaddedSize() const offset = sizeOffset(_padded, _project) if (offset.width !== 0 || offset.height !== 0) { - console.log(`Dotgrid`, `Resize project to ${printSize(_padded)}`) + console.log('Dotgrid', `Resize project to ${printSize(_padded)}`) this.tool.settings.size = _padded } this.update() @@ -179,39 +223,39 @@ function Dotgrid () { reader.onload = function (e) { const data = e.target && e.target.result ? e.target.result : '' - dotgrid.source.load(filename, data) - dotgrid.fitSize() + this.source.load(filename, data) + this.fitSize() } reader.readAsText(file) } this.copy = function (e) { - dotgrid.renderer.update() + this.renderer.update() if (e.target !== this.picker.input) { - e.clipboardData.setData('text/source', dotgrid.tool.export(dotgrid.tool.layer())) - e.clipboardData.setData('text/plain', dotgrid.tool.path()) - e.clipboardData.setData('text/html', dotgrid.manager.el.outerHTML) - e.clipboardData.setData('text/svg+xml', dotgrid.manager.el.outerHTML) + e.clipboardData.setData('text/source', this.tool.export(this.tool.layer())) + e.clipboardData.setData('text/plain', this.tool.path()) + e.clipboardData.setData('text/html', this.manager.el.outerHTML) + e.clipboardData.setData('text/svg+xml', this.manager.el.outerHTML) e.preventDefault() } - dotgrid.renderer.update() + this.renderer.update() } this.cut = function (e) { - dotgrid.renderer.update() + this.renderer.update() if (e.target !== this.picker.input) { - e.clipboardData.setData('text/source', dotgrid.tool.export(dotgrid.tool.layer())) - e.clipboardData.setData('text/plain', dotgrid.tool.export(dotgrid.tool.layer())) - e.clipboardData.setData('text/html', dotgrid.manager.el.outerHTML) - e.clipboardData.setData('text/svg+xml', dotgrid.manager.el.outerHTML) - dotgrid.tool.layers[dotgrid.tool.index] = [] + e.clipboardData.setData('text/source', this.tool.export(this.tool.layer())) + e.clipboardData.setData('text/plain', this.tool.export(this.tool.layer())) + e.clipboardData.setData('text/html', this.manager.el.outerHTML) + e.clipboardData.setData('text/svg+xml', this.manager.el.outerHTML) + this.tool.layers[this.tool.index] = [] e.preventDefault() } - dotgrid.renderer.update() + this.renderer.update() } this.paste = function (e) { @@ -219,12 +263,12 @@ function Dotgrid () { let data = e.clipboardData.getData('text/source') if (isJson(data)) { data = JSON.parse(data.trim()) - dotgrid.tool.import(data) + this.tool.import(data) } e.preventDefault() } - dotgrid.renderer.update() + this.renderer.update() } } diff --git a/desktop/sources/scripts/generator.js b/desktop/sources/scripts/generator.js index 7900d5f..99aa270 100644 --- a/desktop/sources/scripts/generator.js +++ b/desktop/sources/scripts/generator.js @@ -38,9 +38,9 @@ function Generator (layer, style) { for (const id in vertices) { if (skip > 0) { skip -= 1; continue } - let vertex = vertices[parseInt(id)] - let next = vertices[parseInt(id) + 1] - let afterNext = vertices[parseInt(id) + 2] + const vertex = vertices[parseInt(id)] + const next = vertices[parseInt(id) + 1] + const afterNext = vertices[parseInt(id) + 2] if (parseInt(id) === 0 && !prev || parseInt(id) === 0 && prev && (prev.x !== vertex.x || prev.y !== vertex.y)) { html += `M${vertex.x},${vertex.y} ` @@ -49,16 +49,16 @@ function Generator (layer, style) { if (type === 'line') { html += this._line(vertex) } else if (type === 'arc_c') { - let clock = mirror > 0 && mirror < 3 ? '0,0' : '0,1' + const clock = mirror > 0 && mirror < 3 ? '0,0' : '0,1' html += this._arc(vertex, next, clock) } else if (type === 'arc_r') { - let clock = mirror > 0 && mirror < 3 ? '0,1' : '0,0' + const clock = mirror > 0 && mirror < 3 ? '0,1' : '0,0' html += this._arc(vertex, next, clock) } else if (type === 'arc_c_full') { - let clock = mirror > 0 ? '1,0' : '1,1' + const clock = mirror > 0 ? '1,0' : '1,1' html += this._arc(vertex, next, clock) } else if (type === 'arc_r_full') { - let clock = mirror > 0 ? '1,1' : '1,0' + const clock = mirror > 0 ? '1,1' : '1,0' html += this._arc(vertex, next, clock) } else if (type === 'bezier') { html += this._bezier(next, afterNext) diff --git a/desktop/sources/scripts/interface.js b/desktop/sources/scripts/interface.js index 265713a..97ad84e 100644 --- a/desktop/sources/scripts/interface.js +++ b/desktop/sources/scripts/interface.js @@ -16,7 +16,7 @@ function Interface (dotgrid) { this.start = function (host) { let html = '' - let options = { + const options = { cast: { line: { key: 'A', icon: 'M60,60 L240,240' }, arc_c: { key: 'S', icon: 'M60,60 A180,180 0 0,1 240,240' }, @@ -57,7 +57,7 @@ function Interface (dotgrid) { onmouseover="dotgrid.interface.over('${type}','${name}')" viewBox="0 0 300 300" class="icon ${type}"> - ${name === 'depth' ? `` : ''} + ${name === 'depth' ? '' : ''} ${name.capitalize()}${tool.key ? '(' + tool.key + ')' : ''} @@ -89,7 +89,7 @@ function Interface (dotgrid) { this.down = function (type, name, event) { if (!dotgrid.tool[type]) { console.warn(`Unknown option(type): ${type}.${name}`, dotgrid.tool); return } - const mod = event.button == 2 ? -1 : 1; + const mod = event.button === 2 ? -1 : 1 dotgrid.tool[type](name, mod) this.update(true) dotgrid.renderer.update(true) @@ -101,7 +101,7 @@ function Interface (dotgrid) { if (this.prev_operation === dotgrid.cursor.operation && force === false) { return } let multiVertices = null - let segments = dotgrid.tool.layer() + const segments = dotgrid.tool.layer() const sumSegments = dotgrid.tool.length() for (const i in segments) { diff --git a/desktop/sources/scripts/lib/acels.js b/desktop/sources/scripts/lib/acels.js new file mode 100644 index 0000000..17d9c9a --- /dev/null +++ b/desktop/sources/scripts/lib/acels.js @@ -0,0 +1,115 @@ +'use strict' + +function Acels () { + this.all = {} + + this.install = (host = window) => { + host.addEventListener('keydown', this.onKeyDown, false) + host.addEventListener('keyup', this.onKeyUp, false) + } + + this.set = (cat, name, accelerator, downfn, upfn) => { + if (this.all[accelerator]) { console.warn('Acels', `Trying to overwrite ${this.all[accelerator].name}, with ${name}.`) } + this.all[accelerator] = { cat, name, downfn, upfn, accelerator } + } + + this.get = (accelerator) => { + return this.all[accelerator] + } + + this.sort = () => { + const h = {} + for (const item of Object.values(this.all)) { + if (!h[item.cat]) { h[item.cat] = [] } + h[item.cat].push(item) + } + return h + } + + this.convert = (event) => { + const accelerator = event.key.substr(0, 1).toUpperCase() + event.key.substr(1) + if ((event.ctrlKey || event.metaKey) && event.shiftKey) { + return `CmdOrCtrl+Shift+${accelerator}` + } + if (event.shiftKey) { + return `Shift+${accelerator}` + } + if (event.ctrlKey || event.metaKey) { + return `CmdOrCtrl+${accelerator}` + } + return accelerator + } + + this.onKeyDown = (e) => { + const target = this.get(this.convert(e)) + if (!target || !target.downfn) { return } + target.downfn() + e.preventDefault() + } + + this.onKeyUp = (e) => { + const target = this.get(this.convert(e)) + if (!target || !target.upfn) { return } + target.upfn() + e.preventDefault() + } + + this.toMarkdown = () => { + const cats = this.sort() + let text = '' + for (const cat in cats) { + text += `\n### ${cat}\n\n` + for (const item of cats[cat]) { + text += `- \`${item.accelerator}\`: ${item.info}\n` + } + } + return text.trim() + } + + this.toString = () => { + const cats = this.sort() + let text = '' + for (const cat in cats) { + for (const item of cats[cat]) { + text += `${cat}: ${item.name} | ${item.accelerator}\n` + } + } + return text.trim() + } + + // Electron specifics + + this.inject = (name = 'Untitled') => { + const app = require('electron').remote.app + const injection = [] + + injection.push({ + label: name, + submenu: [ + { label: 'About', click: () => { require('electron').shell.openExternal('https://github.com/hundredrabbits/' + name) } }, + { label: 'Download Themes', click: () => { require('electron').shell.openExternal('https://github.com/hundredrabbits/Themes') } }, + { label: 'Fullscreen', accelerator: 'CmdOrCtrl+Enter', click: () => { app.toggleFullscreen() } }, + { label: 'Hide', accelerator: 'CmdOrCtrl+H', click: () => { app.toggleVisible() } }, + { label: 'Toggle Menubar', accelerator: 'Alt+H', click: () => { app.toggleMenubar() } }, + { label: 'Inspect', accelerator: 'CmdOrCtrl+.', click: () => { app.inspect() } }, + { label: 'Quit', accelerator: 'CmdOrCtrl+Q', click: () => { app.exit() } } + ] + }) + + const sorted = this.sort() + for (const cat of Object.keys(sorted)) { + const submenu = [] + for (const option of sorted[cat]) { + if (option.role) { + submenu.push({ role: option.role }) + } else if (option.type) { + submenu.push({ type: option.type }) + } else { + submenu.push({ label: option.name, accelerator: option.accelerator, click: option.downfn }) + } + } + injection.push({ label: cat, submenu: submenu }) + } + app.injectMenu(injection) + } +} diff --git a/desktop/sources/scripts/lib/controller.js b/desktop/sources/scripts/lib/controller.js deleted file mode 100644 index 44f7f1c..0000000 --- a/desktop/sources/scripts/lib/controller.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict' - -function Controller () { - const fs = require('fs') - const { dialog, app } = require('electron').remote - - this.menu = { default: {} } - this.mode = 'default' - - this.app = require('electron').remote.app - - this.start = function () { - } - - this.add = function (mode, cat, label, fn, accelerator) { - if (!this.menu[mode]) { this.menu[mode] = {} } - if (!this.menu[mode][cat]) { this.menu[mode][cat] = {} } - this.menu[mode][cat][label] = { fn: fn, accelerator: accelerator } - } - - this.addRole = function (mode, cat, label) { - if (!this.menu[mode]) { this.menu[mode] = {} } - if (!this.menu[mode][cat]) { this.menu[mode][cat] = {} } - this.menu[mode][cat][label] = { role: label } - } - - this.clearCat = function (mode, cat) { - if (this.menu[mode]) { this.menu[mode][cat] = {} } - } - - this.set = function (mode = 'default') { - this.mode = mode - this.commit() - } - - this.format = function () { - const f = [] - const m = this.menu[this.mode] - for (const cat in m) { - const submenu = [] - for (const name in m[cat]) { - const option = m[cat][name] - if (option.role) { - submenu.push({ role: option.role }) - } else { - submenu.push({ label: name, accelerator: option.accelerator, click: option.fn }) - } - } - f.push({ label: cat, submenu: submenu }) - } - return f - } - - this.commit = function () { - this.app.injectMenu(this.format()) - } - - this.accelerator_for_key = function (key, menu) { - const acc = { basic: null, ctrl: null } - for (cat in menu) { - const options = menu[cat] - for (const id in options.submenu) { - const option = options.submenu[id]; if (option.role) { continue } - acc.basic = (option.accelerator.toLowerCase() === key.toLowerCase()) ? option.label.toUpperCase().replace('TOGGLE ', '').substr(0, 8).trim() : acc.basic - acc.ctrl = (option.accelerator.toLowerCase() === ('CmdOrCtrl+' + key).toLowerCase()) ? option.label.toUpperCase().replace('TOGGLE ', '').substr(0, 8).trim() : acc.ctrl - } - } - return acc - } -} - -module.exports = new Controller() diff --git a/desktop/sources/scripts/lib/source.js b/desktop/sources/scripts/lib/source.js new file mode 100644 index 0000000..8176c61 --- /dev/null +++ b/desktop/sources/scripts/lib/source.js @@ -0,0 +1,69 @@ +'use strict' + +/* global FileReader */ +/* global MouseEvent */ + +function Source () { + this.cache = {} + + this.install = () => { + } + + this.start = () => { + this.new() + } + + this.new = () => { + console.log('Source', 'New file..') + this.cache = {} + } + + this.open = (ext, callback) => { + console.log('Source', 'Open file..') + const input = document.createElement('input') + input.type = 'file' + input.onchange = (e) => { + const file = e.target.files[0] + if (file.name.indexOf(ext) < 0) { console.warn('Source', 'File is not ' + ext); return } + this.cache = file + this.load(this.cache, callback) + } + input.click() + } + + this.save = (name, content, type = 'text/plain', callback) => { + this.saveAs(name, content, type, callback) + } + + this.saveAs = (name, content, type = 'text/plain', callback) => { + console.log('Source', 'Save new file..') + this.download(name, content, type, callback) + } + + this.revert = () => { + + } + + // I/O + + this.load = (file, callback) => { + const reader = new FileReader() + reader.onload = (event) => { + const res = event.target.result + callback(res) + } + reader.readAsText(file, 'UTF-8') + } + + this.download = (name, content, type) => { + console.info('Source', `Downloading ${name}(${type})`) + const link = document.createElement('a') + link.setAttribute('download', name) + if (type === 'image/png' || type === 'image/jpeg') { + link.setAttribute('href', content) + } else { + link.setAttribute('href', 'data:' + type + ';charset=utf-8,' + encodeURIComponent(content)) + } + link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })) + } +} diff --git a/desktop/sources/scripts/lib/theme.js b/desktop/sources/scripts/lib/theme.js index 87e3ab3..94061ed 100644 --- a/desktop/sources/scripts/lib/theme.js +++ b/desktop/sources/scripts/lib/theme.js @@ -1,19 +1,25 @@ 'use strict' -function Theme (_default) { +/* global localStorage */ +/* global FileReader */ +/* global DOMParser */ + +function Theme () { const themer = this - this.active = _default + this.default = { background: '#eee', f_high: '#000', f_med: '#999', f_low: '#ccc', f_inv: '#000', b_high: '#000', b_med: '#888', b_low: '#aaa', b_inv: '#ffb545' } + this.active = {} this.el = document.createElement('style') this.el.type = 'text/css' - this.install = function (host = document.body, callback) { + this.install = (host = document.body, callback) => { host.appendChild(this.el) this.callback = callback } - this.start = function () { + this.start = () => { + this.active = this.default console.log('Theme', 'Starting..') if (isJson(localStorage.theme)) { const storage = JSON.parse(localStorage.theme) @@ -23,13 +29,13 @@ function Theme (_default) { return } } - this.load(_default) + this.load(this.active) } - this.load = function (data) { + this.load = (data) => { const theme = parse(data) - if (!validate(theme)) { console.warn('Theme', 'Not a theme', theme); return } - console.log('Theme', `Loaded theme!`) + if (!validate(theme)) { return } + console.log('Theme', 'Loaded theme!') this.el.innerHTML = `:root { --background: ${theme.background}; --f_high: ${theme.f_high}; --f_med: ${theme.f_med}; --f_low: ${theme.f_low}; --f_inv: ${theme.f_inv}; --b_high: ${theme.b_high}; --b_med: ${theme.b_med}; --b_low: ${theme.b_low}; --b_inv: ${theme.b_inv}; }` localStorage.setItem('theme', JSON.stringify(theme)) this.active = theme @@ -38,8 +44,12 @@ function Theme (_default) { } } - this.reset = function () { - this.load(_default) + this.reset = () => { + this.load(this.default) + } + + this.get = (key) => { + return this.active[key] } function parse (any) { @@ -49,18 +59,18 @@ function Theme (_default) { // Drag - this.drag = function (e) { + this.drag = (e) => { e.stopPropagation() e.preventDefault() e.dataTransfer.dropEffect = 'copy' } - this.drop = function (e) { + this.drop = (e) => { e.preventDefault() e.stopPropagation() const file = e.dataTransfer.files[0] if (!file || !file.name) { console.warn('Theme', 'Unnamed file.'); return } - if (file.name.indexOf('.thm') < 0 && file.name.indexOf('.svg') < 0) { console.warn('Theme', 'Skipped, not a theme'); return } + if (file.name.indexOf('.thm') < 0 && file.name.indexOf('.svg') < 0) { return } const reader = new FileReader() reader.onload = function (e) { themer.load(e.target.result) @@ -68,10 +78,10 @@ function Theme (_default) { reader.readAsText(file) } - this.open = function () { + this.open = () => { const fs = require('fs') const { dialog, app } = require('electron').remote - let paths = dialog.showOpenDialog(app.win, { properties: ['openFile'], filters: [{ name: 'Themes', extensions: ['svg'] }] }) + const paths = dialog.showOpenDialog(app.win, { properties: ['openFile'], filters: [{ name: 'Themes', extensions: ['svg'] }] }) if (!paths) { console.log('Nothing to load'); return } fs.readFile(paths[0], 'utf8', function (err, data) { if (err) throw err @@ -102,15 +112,15 @@ function Theme (_default) { const svg = new DOMParser().parseFromString(text, 'text/xml') try { return { - 'background': svg.getElementById('background').getAttribute('fill'), - 'f_high': svg.getElementById('f_high').getAttribute('fill'), - 'f_med': svg.getElementById('f_med').getAttribute('fill'), - 'f_low': svg.getElementById('f_low').getAttribute('fill'), - 'f_inv': svg.getElementById('f_inv').getAttribute('fill'), - 'b_high': svg.getElementById('b_high').getAttribute('fill'), - 'b_med': svg.getElementById('b_med').getAttribute('fill'), - 'b_low': svg.getElementById('b_low').getAttribute('fill'), - 'b_inv': svg.getElementById('b_inv').getAttribute('fill') + background: svg.getElementById('background').getAttribute('fill'), + f_high: svg.getElementById('f_high').getAttribute('fill'), + f_med: svg.getElementById('f_med').getAttribute('fill'), + f_low: svg.getElementById('f_low').getAttribute('fill'), + f_inv: svg.getElementById('f_inv').getAttribute('fill'), + b_high: svg.getElementById('b_high').getAttribute('fill'), + b_med: svg.getElementById('b_med').getAttribute('fill'), + b_low: svg.getElementById('b_low').getAttribute('fill'), + b_inv: svg.getElementById('b_inv').getAttribute('fill') } } catch (err) { console.warn('Theme', 'Incomplete SVG Theme', err) diff --git a/desktop/sources/scripts/listener.js b/desktop/sources/scripts/listener.js deleted file mode 100644 index 2c5e815..0000000 --- a/desktop/sources/scripts/listener.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict' - -// Dotgrid UDP Listener -// Ex: 1a0156(6 characters 0-9a-z) - -// 0 layer[0-2] -// 1 type[lcrd*] -// 2 from[0-z][0-z] -// 4 to[0-z][0-z] - -const dgram = require('dgram') - -function Listener (dotgrid) { - this.server = dgram.createSocket('udp4') - - function base36 (c) { - return clamp(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y'].indexOf(c.toLowerCase()), 0, 36) * 15 + 15 - } - - function clear () { - dotgrid.tool.erase() - } - - function operate (data) { - if (!data) { return } - if (!dotgrid.tool.layers[data.layer]) { return } - dotgrid.tool.addSegment(data.type, [data.from, data.to], data.layer) - } - - function draw () { - dotgrid.renderer.update(true) - } - - function parse (msg) { - if (msg === '' || msg === '*') { - return draw() - } - if (['0', '1', '2'].indexOf(msg) > -1) { - return clear() - } - const layer = parseInt(msg.substr(0, 1)) - const type = { 'l': 'line', 'c': 'arc_c', 'r': 'arc_r', '*': 'draw' }[msg.substr(1, 1).toLowerCase()] - const from = { x: base36(msg.substr(2, 1)), y: base36(msg.substr(3, 1)) } - const to = { x: base36(msg.substr(4, 1)), y: base36(msg.substr(5, 1)) } - return { layer: layer, type: type, from: from, to: to } - } - - function clamp (v, min, max) { - return v < min ? min : v > max ? max : v - } - - // Server - - this.server.on('message', (msg, rinfo) => { - operate(parse(`${msg}`)) - }) - - this.server.on('listening', () => { - const address = this.server.address() - console.log(`Server listening for UDP:\n ${address.address}:${address.port}`) - }) - - this.server.on('error', (err) => { - console.log(`Server error:\n ${err.stack}`) - server.close() - }) - - this.server.bind(49161) -} diff --git a/desktop/sources/scripts/manager.js b/desktop/sources/scripts/manager.js index fa9611a..3b276df 100644 --- a/desktop/sources/scripts/manager.js +++ b/desktop/sources/scripts/manager.js @@ -30,7 +30,7 @@ function Manager (dotgrid) { for (const id in this.layers) { let style = styles[id] let path = paths[id] - let layer = this.layers[id] + const layer = this.layers[id] // Easter Egg if (dotgrid.tool.settings.crest === true) { style = styles[0] @@ -49,9 +49,9 @@ function Manager (dotgrid) { } this.svg64 = function () { - let xml = new XMLSerializer().serializeToString(this.el) - let svg64 = btoa(xml) - let b64Start = 'data:image/svg+xml;base64,' + const xml = new XMLSerializer().serializeToString(this.el) + const svg64 = btoa(xml) + const b64Start = 'data:image/svg+xml;base64,' return b64Start + svg64 } @@ -60,9 +60,9 @@ function Manager (dotgrid) { this.toPNG = function (size = dotgrid.tool.settings.size, callback) { this.update() - let image64 = this.svg64() - let img = new Image() - let canvas = document.createElement('canvas') + const image64 = this.svg64() + const img = new Image() + const canvas = document.createElement('canvas') canvas.width = (size.width) * 2 canvas.height = (size.height) * 2 img.onload = function () { diff --git a/desktop/sources/scripts/picker.js b/desktop/sources/scripts/picker.js index edcca45..2ad0b06 100644 --- a/desktop/sources/scripts/picker.js +++ b/desktop/sources/scripts/picker.js @@ -18,6 +18,9 @@ function Picker (dotgrid) { this.input.setAttribute('placeholder', `${dotgrid.tool.style().color.replace('#', '').trim()}`) this.input.setAttribute('maxlength', 6) + this.input.addEventListener('keydown', this.onKeyDown, false) + this.input.addEventListener('keyup', this.onKeyUp, false) + dotgrid.interface.el.className = 'picker' this.input.focus() this.input.value = '' @@ -27,7 +30,7 @@ function Picker (dotgrid) { this.update = function () { if (!this.isActive) { return } - if (!is_color(this.input.value)) { return } + if (!isColor(this.input.value)) { return } const hex = `#${this.input.value}` @@ -50,7 +53,7 @@ function Picker (dotgrid) { } this.validate = function () { - if (!is_color(this.input.value)) { return } + if (!isColor(this.input.value)) { return } const hex = `#${this.input.value}` @@ -60,28 +63,7 @@ function Picker (dotgrid) { this.stop() } - this.listen = function (e, is_down = false) { - if (is_down && !isColorChar(e.key)) { - e.preventDefault() - return - } - - if (e.key === 'Enter') { - this.validate() - e.preventDefault() - return - } - - if (e.key === 'Escape') { - this.stop() - e.preventDefault() - return - } - - this.update() - } - - function is_color (val) { + function isColor (val) { if (val.length !== 3 && val.length !== 6) { return false } @@ -90,11 +72,22 @@ function Picker (dotgrid) { return re.test(val) } - function isColorChar (val) { - const re = /[0-9A-Fa-f]/g - return re.test(val) + this.onKeyDown = (e) => { + if (e.key === 'Enter') { + this.validate() + e.preventDefault() + return + } + if (e.key === 'Escape') { + this.stop() + e.preventDefault() + return + } + e.stopPropagation() } - this.input.onkeydown = function (event) { dotgrid.picker.listen(event, true) } - this.input.onkeyup = function (event) { dotgrid.picker.listen(event) } + this.onKeyUp = (e) => { + e.stopPropagation() + this.update() + } } diff --git a/desktop/sources/scripts/renderer.js b/desktop/sources/scripts/renderer.js index dca1186..51c9dac 100644 --- a/desktop/sources/scripts/renderer.js +++ b/desktop/sources/scripts/renderer.js @@ -19,7 +19,7 @@ function Renderer (dotgrid) { this.update = function (force = false) { this.resize() dotgrid.manager.update() - let render = new Image() + const render = new Image() render.onload = () => { this.draw(render) } @@ -106,7 +106,7 @@ function Renderer (dotgrid) { for (let x = markers.w - 1; x >= 0; x--) { for (let y = markers.h - 1; y >= 0; y--) { - let isStep = x % 4 === 0 && y % 4 === 0 + const isStep = x % 4 === 0 && y % 4 === 0 // Don't draw margins if (x === 0 || y === 0) { continue } this.drawMarker({ @@ -129,13 +129,13 @@ function Renderer (dotgrid) { } this.drawPreview = function () { - let operation = dotgrid.cursor.operation && dotgrid.cursor.operation.cast ? dotgrid.cursor.operation.cast : null + const operation = dotgrid.cursor.operation && dotgrid.cursor.operation.cast ? dotgrid.cursor.operation.cast : null if (!dotgrid.tool.canCast(operation)) { return } if (operation === 'close') { return } - let path = new Generator([{ vertices: dotgrid.tool.vertices, type: operation }]).toString({ x: 0, y: 0 }, 2) - let style = { + const path = new Generator([{ vertices: dotgrid.tool.vertices, type: operation }]).toString({ x: 0, y: 0 }, 2) + const style = { color: dotgrid.theme.active.f_med, thickness: 2, strokeLinecap: 'round', @@ -201,7 +201,7 @@ function Renderer (dotgrid) { } this.drawPath = function (path, style) { - let p = new Path2D(path) + const p = new Path2D(path) this.context.strokeStyle = style.color this.context.lineWidth = style.thickness * this.scale diff --git a/desktop/sources/scripts/source.js b/desktop/sources/scripts/source.js index 2b12a39..bf6102b 100644 --- a/desktop/sources/scripts/source.js +++ b/desktop/sources/scripts/source.js @@ -65,7 +65,7 @@ function Source (dotgrid) { const link = document.createElement('a') link.setAttribute('href', base64) link.setAttribute('download', name) - link.dispatchEvent(new MouseEvent(`click`, { bubbles: true, cancelable: true, view: window })) + link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })) } function isJson (text) { try { JSON.parse(text); return true } catch (error) { return false } } diff --git a/desktop/sources/scripts/tool.js b/desktop/sources/scripts/tool.js index 187b4af..2854769 100644 --- a/desktop/sources/scripts/tool.js +++ b/desktop/sources/scripts/tool.js @@ -102,9 +102,9 @@ function Tool (dotgrid) { this.removeSegmentsAt = function (pos) { for (const segmentId in this.layer()) { - let segment = this.layer()[segmentId] + const segment = this.layer()[segmentId] for (const vertexId in segment.vertices) { - let vertex = segment.vertices[vertexId] + const vertex = segment.vertices[vertexId] if (Math.abs(pos.x) === Math.abs(vertex.x) && Math.abs(pos.y) === Math.abs(vertex.y)) { segment.vertices.splice(vertexId, 1) } @@ -119,11 +119,11 @@ function Tool (dotgrid) { } this.selectSegmentAt = function (pos, source = this.layer()) { - let target_segment = null + const target_segment = null for (const segmentId in source) { - let segment = source[segmentId] + const segment = source[segmentId] for (const vertexId in segment.vertices) { - let vertex = segment.vertices[vertexId] + const vertex = segment.vertices[vertexId] if (vertex.x === Math.abs(pos.x) && vertex.y === Math.abs(pos.y)) { return segment } @@ -140,9 +140,9 @@ function Tool (dotgrid) { this.vertexAt = function (pos) { for (const segmentId in this.layer()) { - let segment = this.layer()[segmentId] + const segment = this.layer()[segmentId] for (const vertexId in segment.vertices) { - let vertex = segment.vertices[vertexId] + const vertex = segment.vertices[vertexId] if (vertex.x === Math.abs(pos.x) && vertex.y === Math.abs(pos.y)) { return vertex } @@ -152,7 +152,7 @@ function Tool (dotgrid) { } this.addSegment = function (type, vertices, index = this.index) { - let append_target = this.canAppend({ type: type, vertices: vertices }, index) + const append_target = this.canAppend({ type: type, vertices: vertices }, index) if (append_target) { this.layer(index)[append_target].vertices = this.layer(index)[append_target].vertices.concat(vertices) } else { @@ -179,11 +179,11 @@ function Tool (dotgrid) { this.toggle = function (type, mod = 1) { if (type === 'linecap') { - let a = ['butt', 'square', 'round'] + const a = ['butt', 'square', 'round'] this.i.linecap += mod this.style().strokeLinecap = a[this.i.linecap % a.length] } else if (type === 'linejoin') { - let a = ['miter', 'round', 'bevel'] + const a = ['miter', 'round', 'bevel'] this.i.linejoin += mod this.style().strokeLinejoin = a[this.i.linejoin % a.length] } else if (type === 'fill') { @@ -221,7 +221,7 @@ function Tool (dotgrid) { this.canAppend = function (content, index = this.index) { for (const id in this.layer(index)) { - let stroke = this.layer(index)[id] + const stroke = this.layer(index)[id] if (stroke.type !== content.type) { continue } if (!stroke.vertices) { continue } if (!stroke.vertices[stroke.vertices.length - 1]) { continue } @@ -236,7 +236,7 @@ function Tool (dotgrid) { if (!type) { return false } // Cannot cast close twice if (type === 'close') { - let prev = this.layer()[this.layer().length - 1] + const prev = this.layer()[this.layer().length - 1] if (!prev || prev.type === 'close') { return false } @@ -250,9 +250,9 @@ function Tool (dotgrid) { } this.paths = function () { - let l1 = new Generator(dotgrid.tool.layers[0], dotgrid.tool.styles[0]).toString({ x: 0, y: 0 }, 1) - let l2 = new Generator(dotgrid.tool.layers[1], dotgrid.tool.styles[1]).toString({ x: 0, y: 0 }, 1) - let l3 = new Generator(dotgrid.tool.layers[2], dotgrid.tool.styles[2]).toString({ x: 0, y: 0 }, 1) + const l1 = new Generator(dotgrid.tool.layers[0], dotgrid.tool.styles[0]).toString({ x: 0, y: 0 }, 1) + const l2 = new Generator(dotgrid.tool.layers[1], dotgrid.tool.styles[1]).toString({ x: 0, y: 0 }, 1) + const l3 = new Generator(dotgrid.tool.layers[2], dotgrid.tool.styles[2]).toString({ x: 0, y: 0 }, 1) return [l1, l2, l3] } @@ -263,9 +263,9 @@ function Tool (dotgrid) { this.translate = function (a, b) { for (const segmentId in this.layer()) { - let segment = this.layer()[segmentId] + const segment = this.layer()[segmentId] for (const vertexId in segment.vertices) { - let vertex = segment.vertices[vertexId] + const vertex = segment.vertices[vertexId] if (vertex.x === Math.abs(a.x) && vertex.y === Math.abs(a.y)) { segment.vertices[vertexId] = { x: Math.abs(b.x), y: Math.abs(b.y) } } @@ -283,7 +283,7 @@ function Tool (dotgrid) { if (!segment) { return } for (const vertexId in segment.vertices) { - let vertex = segment.vertices[vertexId] + const vertex = segment.vertices[vertexId] segment.vertices[vertexId] = { x: vertex.x - offset.x, y: vertex.y - offset.y } } @@ -295,9 +295,9 @@ function Tool (dotgrid) { this.translateLayer = function (a, b) { const offset = { x: a.x - b.x, y: a.y - b.y } for (const segmentId in this.layer()) { - let segment = this.layer()[segmentId] + const segment = this.layer()[segmentId] for (const vertexId in segment.vertices) { - let vertex = segment.vertices[vertexId] + const vertex = segment.vertices[vertexId] segment.vertices[vertexId] = { x: vertex.x - offset.x, y: vertex.y - offset.y } } } @@ -313,7 +313,7 @@ function Tool (dotgrid) { if (!segment) { return } for (const vertexId in segment.vertices) { - let vertex = segment.vertices[vertexId] + const vertex = segment.vertices[vertexId] segment.vertices[vertexId] = { x: vertex.x - offset.x, y: vertex.y - offset.y } } this.layer().push(segment) diff --git a/index.html b/index.html index d55eb1c..4f37fa8 100644 --- a/index.html +++ b/index.html @@ -19,6 +19,7 @@ + @@ -30,9 +31,7 @@ - - - + @@ -42,14 +41,15 @@