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 @@