diff --git a/bin/browser.sh b/bin/browser.sh old mode 100755 new mode 100644 diff --git a/browser/menu.js b/browser/menu.js index c20845b..7553aa3 100644 --- a/browser/menu.js +++ b/browser/menu.js @@ -1,629 +1,629 @@ -/* globals require, process */ -/*eslint no-console: ["error", { allow: ["log", "error"] }] */ - -const { Menu, app, shell, dialog } = require('electron') -const fs = require('fs') -const fse = require('fs-extra') -const os = require('os') -const path = require('path') -const { openProcessManager } = require('electron-process-manager') -const main = require('./main.js') - -let { thumbnail } = require('./utils.js') -const loadTests = require('./test.js') -const i18n = new (require('./i18n.js'))() - -function selectURL(url) { - url = url.replace(/\\/g, '/') - console.log('selectURL', url) - main.win.loadURL(url) - main.store.set('url', url) -} - -function findItems(key, value) { - let items = [] - - for (let i = 0; i < menu.items.length; i++) { - for (let j = 0; j < menu.items[i].submenu.items.length; j++) { - let item = menu.items[i].submenu.items[j] - if (item[key] === value) { - items.push(item) - } - } - } - - return items -} - -function findItem(key, value) { - return findItems(key, value)[0] -} - -function toggleBookmarks(bookmark) { - let items = findItems('class', 'bookmark') - - for (let i = 0; i < items.length; i++) { - items[i].checked = false - } - - bookmark.checked = true -} - -function checkBookmark(url) { - let items = findItems('url', url) - if (items.length === 1) { - toggleBookmarks(items[0]) - } -} - -function setHistoryStatus() { - const historyBack = findItem('id', 'history-back') - historyBack.enabled = main.win.webContents.canGoBack() - - const historyForward = findItem('id', 'history-forward') - historyForward.enabled = main.win.webContents.canGoForward() -} - -function showSelectDataFolderDialog(focusedWindow) { - dialog.showOpenDialog( - { - title: i18n.__('selectfolder.select.title'), - buttonLabel: i18n.__('selectfolder.select.buttonLabel'), - properties: ['openDirectory', 'createDirectory', 'noResolveAliases', 'treatPackageAsDirectory'] - }, - (filePaths) => { - if (filePaths && filePaths.length === 1) { - const varPath = path.join(__dirname, '../var') - - // Check if the same folder was used - if (filePaths[0].startsWith(varPath)) { - const same = filePaths[0] === varPath - - dialog.showMessageBox( - { - type: 'error', - icon: path.join(__dirname, '../assets/icons/png/512x512-empty.png'), - buttons: [i18n.__('selectfolder.samefolder.ok')], - defaultId: 0, - message: i18n.__('selectfolder.samefolder.message'), - detail: same - ? i18n.__('selectfolder.samefolder.detail.same') - : i18n.__('selectfolder.samefolder.detail.within'), - cancelId: 0 - }, - (response) => { - showSelectDataFolderDialog(focusedWindow) - } - ) - } else { - // Backup - if (fse.pathExistsSync(varPath)) { - const varPathBackup = findNextVarFolder() - // Rename old var folder or link - fse.renameSync(varPath, varPathBackup) - } else { - // BUG: Workaround because pathExistsSync return false on existing symbolic links with a missing target - fse.removeSync(varPath) - } - - // Add new symlink - main.store.set('dataFolder', filePaths[0]) - fs.symlinkSync(filePaths[0], varPath, 'dir') - - dialog.showMessageBox( - { - type: 'info', - icon: path.join(__dirname, '../assets/icons/png/link.png'), - buttons: [i18n.__('selectfolder.info.ok')], - defaultId: 0, - message: i18n.__('selectfolder.info.message'), - detail: i18n.__('selectfolder.info.detail').replace(/\$\{0\}/, filePaths[0]), - cancelId: 0 - }, - (response) => { - if (focusedWindow) focusedWindow.reload() - } - ) - } - } - } - ) -} - -function findNextVarFolder() { - let exists = true - let counter = 0 - - while (exists) { - counter++ - exists = fse.pathExistsSync(path.join(__dirname, `../var${counter}`)) - } - - return path.join(__dirname, `../var${counter}`) -} - -function showFolderBrowser(focusedWindow) { - const varPath = path.join(__dirname, '../var') - const varPathExists = fse.pathExistsSync(varPath) - if (varPathExists) { - dialog.showMessageBox( - { - type: 'warning', - icon: path.join(__dirname, '../assets/icons/png/512x512-empty.png'), - buttons: [i18n.__('selectfolder.warning.next'), i18n.__('selectfolder.warning.cancel')], - defaultId: 1, - message: i18n.__('selectfolder.warning.message'), - detail: i18n.__('selectfolder.warning.detail'), - cancelId: 1 - }, - (response) => { - if (response === 0) { - showSelectDataFolderDialog(focusedWindow) - } - } - ) - } else { - showSelectDataFolderDialog(focusedWindow) - } -} - -const template = [ - { - label: i18n.__('edit'), - submenu: [ - { - role: 'undo', - label: i18n.__('undo') - }, - { - role: 'redo', - label: i18n.__('redo') - }, - { - type: 'separator' - }, - { - role: 'cut', - label: i18n.__('cut') - }, - { - role: 'copy', - label: i18n.__('copy') - }, - { - role: 'paste', - label: i18n.__('paste') - }, - { - role: 'pasteandmatchstyle', - label: i18n.__('pasteandmatchstyle') - }, - { - role: 'delete', - label: i18n.__('delete') - }, - { - role: 'selectall', - label: i18n.__('selectall') - } - ] - }, - { - label: i18n.__('view'), - submenu: [ - { - label: i18n.__('reload'), - accelerator: 'CmdOrCtrl+R', - click(item, focusedWindow) { - if (focusedWindow) { - focusedWindow.webContents.setVisualZoomLevelLimits(1, 1) - focusedWindow.reload() - } - } - }, - { - id: 'forcereload', - label: i18n.__('forcereload'), - accelerator: 'CmdOrCtrl+Shift+R', - click(item, focusedWindow) { - if (focusedWindow) { - focusedWindow.webContents.session.clearCache(() => console.log('Cache cleared')) - - focusedWindow.webContents.setVisualZoomLevelLimits(1, 1) - focusedWindow.reload() - } - } - }, - { - type: 'separator' - }, - { - role: 'resetzoom', - label: i18n.__('resetzoom') - }, - { - role: 'zoomin', - label: i18n.__('zoomin') - }, - { - role: 'zoomout', - label: i18n.__('zoomout') - }, - { - type: 'separator' - }, - { - id: 'togglefullscreen', - label: i18n.__('togglefullscreen'), - accelerator: process.platform === 'darwin' ? 'Cmd+Ctrl+F' : 'F11', - click(item, focusedWindow) { - if (focusedWindow) { - focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) - } - } - }, - { - type: 'separator' - }, - { - label: i18n.__('multiuserbrowser'), - accelerator: 'CmdOrCtrl+M', - type: 'checkbox', - checked: true, - click(item, focusedWindow) { - if (focusedWindow) { - main.store.set('multiUserBrowser', item.checked) - global.multiUserMode = item.checked - } - } - }, - { - label: i18n.__('minimalpad'), - accelerator: 'CmdOrCtrl+p', - type: 'checkbox', - checked: true, - click(item, focusedWindow) { - if (focusedWindow) { - main.store.set('minimalPad', item.checked) - global.useMinimalPad = item.checked - } - } - } - ] - }, - { - label: i18n.__('history'), - submenu: [ - { - id: 'history-back', - label: i18n.__('back'), - accelerator: 'CmdOrCtrl+Left', - click(item, focusedWindow) { - main.win.webContents.goBack() - } - }, - { - id: 'history-forward', - label: i18n.__('forward'), - accelerator: 'CmdOrCtrl+Right', - click(item, focusedWindow) { - main.win.webContents.goForward() - } - }, - { - label: i18n.__('home'), - accelerator: 'CmdOrCtrl+Up', - click(item, focusedWindow) { - main.win.webContents.goToIndex(0) - } - }, - { - type: 'separator' - }, - { - label: i18n.__('recentlyvisited'), - enabled: false - } - ] - }, - { - label: i18n.__('bookmarks'), - submenu: [ - { - label: i18n.__('localfilesystem'), - class: 'bookmark', - type: 'checkbox', - url: `file://${__dirname}/../index.html`, - accelerator: 'CmdOrCtrl+L', - click(item, focusedWindow) { - selectURL(item.url) - toggleBookmarks(item) - } - }, - { - label: i18n.__('testframes'), - class: 'bookmark', - type: 'checkbox', - url: `file://${__dirname}/../index.html?test`, - accelerator: 'CmdOrCtrl+T', - click(item, focusedWindow) { - selectURL(item.url) - toggleBookmarks(item) - } - }, - { - type: 'separator' - }, - { - id: 'localhost', - label: 'https://localhost:8443', - class: 'bookmark', - type: 'checkbox', - enabled: false, - url: 'https://localhost:8443/index.html', - click(item, focusedWindow) { - selectURL(item.url) - toggleBookmarks(item) - } - }, - { - id: 'localhost', - label: 'https://localhost:3000', - class: 'bookmark', - type: 'checkbox', - enabled: true, - url: 'https://localhost:3000/index.html', - click(item, focusedWindow) { - selectURL(item.url) - toggleBookmarks(item) - } - }, - // { - // label: 'http://tornado.iwm-kmrc.de:8000', - // class: 'bookmark', - // type: 'checkbox', - // url: 'http://tornado.iwm-kmrc.de:8000/index.html', - // click(item, focusedWindow) { - // selectURL(item.url) - // toggleBookmarks(item) - // } - // }, - { - label: 'http://rousseau.iwm-kmrc.de/index.html', - class: 'bookmark', - type: 'checkbox', - url: 'http://rousseau.iwm-kmrc.de/index.html', - click(item, focusedWindow) { - selectURL(item.url) - toggleBookmarks(item) - } - } - ] - }, - { - label: i18n.__('develop'), - submenu: [ - { - id: 'toggledevelopertools', - label: i18n.__('toggledevelopertools'), - accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', - click(item, focusedWindow) { - if (focusedWindow) focusedWindow.webContents.toggleDevTools() - } - }, - { - label: i18n.__('openprocessmonitor'), - accelerator: process.platform === 'darwin' ? 'Alt+Command+P' : 'Ctrl+Shift+P', - click(item, focusedWindow) { - openProcessManager() - } - }, - { - type: 'separator' - }, - { - label: i18n.__('selectfolder'), - accelerator: process.platform === 'darwin' ? 'Alt+Command+D' : 'Ctrl+Shift+D', - click(item, focusedWindow) { - if (process.platform === 'win32') { - var exec = require('child_process').exec - exec('NET SESSION', function (err, so, se) { - const admin = se.length === 0 ? true : false - - if (admin) { - showFolderBrowser(focusedWindow) - } else { - dialog.showMessageBox({ - type: 'error', - icon: path.join(__dirname, '../assets/icons/png/512x512-empty.png'), - buttons: [i18n.__('selectfolder.noadmin.ok')], - message: i18n.__('selectfolder.noadmin.message'), - detail: i18n.__('selectfolder.noadmin.detail') - }) - } - }) - } else { - showFolderBrowser(focusedWindow) - } - } - }, - { - type: 'separator' - }, - { - id: 'startserver', - label: i18n.__('startserver'), - accelerator: process.platform === 'darwin' ? 'Alt+Command+S' : 'Ctrl+Shift+S', - click(item, focusedWindow) { - const { server } = require('../server/main.js') - server.start() - item.visible = false - findItem('id', 'stopserver').visible = true - findItem('id', 'localhost').enabled = true - } - }, - { - id: 'stopserver', - label: i18n.__('stopserver'), - accelerator: process.platform === 'darwin' ? 'Alt+Command+S' : 'Ctrl+Shift+S', - visible: false, - click(item, focusedWindow) { - const { server } = require('../server/main.js') - server.stop() - item.visible = false - findItem('id', 'startserver').visible = true - findItem('id', 'localhost').enabled = false - } - }, - { - type: 'separator' - }, - { - label: i18n.__('runloadtests'), - accelerator: process.platform === 'darwin' ? 'Alt+Command+L' : 'Ctrl+Shift+L', - click(item, focusedWindow) { - loadTests(focusedWindow) - } - }, - { - type: 'separator' - }, - { - label: 'Aktualisiere Tüsch POIs', - click(item, focusedWindow) { - const UpdatePOI = require('../dev/tuesch/bin/menu/update-pois.js') - UpdatePOI.update('./dev/tuesch') - } - } - ] - }, - { - role: 'window', - label: i18n.__('window'), - submenu: [ - { - role: 'close', - label: i18n.__('close') - }, - { - role: 'minimize', - label: i18n.__('minimize') - }, - { - role: 'zoom', - label: i18n.__('zoom') - }, - { - type: 'separator' - }, - { - role: 'front', - label: i18n.__('front') - }, - { - type: 'separator' - }, - { - label: i18n.__('screenshot'), - accelerator: 'CmdOrCtrl+S', - async click(item, focusedWindow) { - if (focusedWindow) { - await focusedWindow.webContents.capturePage().then((image) => { - let screenshotFile = path.join(os.tmpdir(), 'screenshot.png') - - console.log('image captured', screenshotFile) - - let url = focusedWindow.webContents.getURL() - if (url.startsWith('file://')) { - let normalized = path.normalize(url).replace('.html', '.png') - screenshotFile = normalized.replace('file:', '') - let thumbnailFile = screenshotFile.replace('index.png', 'thumbnail.png') - if (url.endsWith('index.html')) { - thumbnailFile = screenshotFile.replace('index.png', 'thumbnail.png') - } else { - let folderName = path.dirname(screenshotFile) - let baseName = path.basename(screenshotFile) - thumbnailFile = path.join(folderName, 'thumbnails', baseName) - } - fs.writeFile(thumbnailFile, thumbnail(image), (err) => { - if (err) { - throw err - } else { - console.log(`Thumbnail written to ${thumbnailFile}`) - } - }) - } - fs.writeFile(screenshotFile, image.toPNG(), (err) => { - if (err) { - throw err - } else { - console.log(`Screenshot written to ${screenshotFile}`) - } - }) - }) - } - } - }, - { - type: 'separator' - } - ] - }, - { - role: 'help', - label: i18n.__('help'), - submenu: [ - { - label: i18n.__('iwm'), - click() { - shell.openExternal('https://www.iwm-tuebingen.de') - } - } - ] - } -] - -if (process.platform === 'darwin') { - const name = app.getName() - template.unshift({ - label: name, - submenu: [ - { - role: 'about', - label: i18n.__('about') - }, - { - type: 'separator' - }, - { - role: 'quit', - label: i18n.__('quit') - } - ] - }) -} - -const menu = Menu.buildFromTemplate(template) -Menu.setApplicationMenu(menu) - -checkBookmark(main.store.get('url')) -setHistoryStatus() - -function focus() { - findItem('id', 'forcereload').enabled = true - findItem('id', 'togglefullscreen').enabled = true - findItem('id', 'toggledevelopertools').enabled = true -} - -function blur() { - findItem('id', 'forcereload').enabled = false - findItem('id', 'togglefullscreen').enabled = false - findItem('id', 'toggledevelopertools').enabled = false -} - -module.exports = { - menu, - setHistoryStatus, - focus, - blur -} +/* globals require, process */ +/*eslint no-console: ["error", { allow: ["log", "error"] }] */ + +const { Menu, app, shell, dialog } = require('electron') +const fs = require('fs') +const fse = require('fs-extra') +const os = require('os') +const path = require('path') +const { openProcessManager } = require('electron-process-manager') +const main = require('./main.js') + +let { thumbnail } = require('./utils.js') +const loadTests = require('./test.js') +const i18n = new (require('./i18n.js'))() + +function selectURL(url) { + url = url.replace(/\\/g, '/') + console.log('selectURL', url) + main.win.loadURL(url) + main.store.set('url', url) +} + +function findItems(key, value) { + let items = [] + + for (let i = 0; i < menu.items.length; i++) { + for (let j = 0; j < menu.items[i].submenu.items.length; j++) { + let item = menu.items[i].submenu.items[j] + if (item[key] === value) { + items.push(item) + } + } + } + + return items +} + +function findItem(key, value) { + return findItems(key, value)[0] +} + +function toggleBookmarks(bookmark) { + let items = findItems('class', 'bookmark') + + for (let i = 0; i < items.length; i++) { + items[i].checked = false + } + + bookmark.checked = true +} + +function checkBookmark(url) { + let items = findItems('url', url) + if (items.length === 1) { + toggleBookmarks(items[0]) + } +} + +function setHistoryStatus() { + const historyBack = findItem('id', 'history-back') + historyBack.enabled = main.win.webContents.canGoBack() + + const historyForward = findItem('id', 'history-forward') + historyForward.enabled = main.win.webContents.canGoForward() +} + +function showSelectDataFolderDialog(focusedWindow) { + dialog.showOpenDialog( + { + title: i18n.__('selectfolder.select.title'), + buttonLabel: i18n.__('selectfolder.select.buttonLabel'), + properties: ['openDirectory', 'createDirectory', 'noResolveAliases', 'treatPackageAsDirectory'] + }, + (filePaths) => { + if (filePaths && filePaths.length === 1) { + const varPath = path.join(__dirname, '../var') + + // Check if the same folder was used + if (filePaths[0].startsWith(varPath)) { + const same = filePaths[0] === varPath + + dialog.showMessageBox( + { + type: 'error', + icon: path.join(__dirname, '../assets/icons/png/512x512-empty.png'), + buttons: [i18n.__('selectfolder.samefolder.ok')], + defaultId: 0, + message: i18n.__('selectfolder.samefolder.message'), + detail: same + ? i18n.__('selectfolder.samefolder.detail.same') + : i18n.__('selectfolder.samefolder.detail.within'), + cancelId: 0 + }, + (response) => { + showSelectDataFolderDialog(focusedWindow) + } + ) + } else { + // Backup + if (fse.pathExistsSync(varPath)) { + const varPathBackup = findNextVarFolder() + // Rename old var folder or link + fse.renameSync(varPath, varPathBackup) + } else { + // BUG: Workaround because pathExistsSync return false on existing symbolic links with a missing target + fse.removeSync(varPath) + } + + // Add new symlink + main.store.set('dataFolder', filePaths[0]) + fs.symlinkSync(filePaths[0], varPath, 'dir') + + dialog.showMessageBox( + { + type: 'info', + icon: path.join(__dirname, '../assets/icons/png/link.png'), + buttons: [i18n.__('selectfolder.info.ok')], + defaultId: 0, + message: i18n.__('selectfolder.info.message'), + detail: i18n.__('selectfolder.info.detail').replace(/\$\{0\}/, filePaths[0]), + cancelId: 0 + }, + (response) => { + if (focusedWindow) focusedWindow.reload() + } + ) + } + } + } + ) +} + +function findNextVarFolder() { + let exists = true + let counter = 0 + + while (exists) { + counter++ + exists = fse.pathExistsSync(path.join(__dirname, `../var${counter}`)) + } + + return path.join(__dirname, `../var${counter}`) +} + +function showFolderBrowser(focusedWindow) { + const varPath = path.join(__dirname, '../var') + const varPathExists = fse.pathExistsSync(varPath) + if (varPathExists) { + dialog.showMessageBox( + { + type: 'warning', + icon: path.join(__dirname, '../assets/icons/png/512x512-empty.png'), + buttons: [i18n.__('selectfolder.warning.next'), i18n.__('selectfolder.warning.cancel')], + defaultId: 1, + message: i18n.__('selectfolder.warning.message'), + detail: i18n.__('selectfolder.warning.detail'), + cancelId: 1 + }, + (response) => { + if (response === 0) { + showSelectDataFolderDialog(focusedWindow) + } + } + ) + } else { + showSelectDataFolderDialog(focusedWindow) + } +} + +const template = [ + { + label: i18n.__('edit'), + submenu: [ + { + role: 'undo', + label: i18n.__('undo') + }, + { + role: 'redo', + label: i18n.__('redo') + }, + { + type: 'separator' + }, + { + role: 'cut', + label: i18n.__('cut') + }, + { + role: 'copy', + label: i18n.__('copy') + }, + { + role: 'paste', + label: i18n.__('paste') + }, + { + role: 'pasteandmatchstyle', + label: i18n.__('pasteandmatchstyle') + }, + { + role: 'delete', + label: i18n.__('delete') + }, + { + role: 'selectall', + label: i18n.__('selectall') + } + ] + }, + { + label: i18n.__('view'), + submenu: [ + { + label: i18n.__('reload'), + accelerator: 'CmdOrCtrl+R', + click(item, focusedWindow) { + if (focusedWindow) { + focusedWindow.webContents.setVisualZoomLevelLimits(1, 1) + focusedWindow.reload() + } + } + }, + { + id: 'forcereload', + label: i18n.__('forcereload'), + accelerator: 'CmdOrCtrl+Shift+R', + click(item, focusedWindow) { + if (focusedWindow) { + focusedWindow.webContents.session.clearCache(() => console.log('Cache cleared')) + + focusedWindow.webContents.setVisualZoomLevelLimits(1, 1) + focusedWindow.reload() + } + } + }, + { + type: 'separator' + }, + { + role: 'resetzoom', + label: i18n.__('resetzoom') + }, + { + role: 'zoomin', + label: i18n.__('zoomin') + }, + { + role: 'zoomout', + label: i18n.__('zoomout') + }, + { + type: 'separator' + }, + { + id: 'togglefullscreen', + label: i18n.__('togglefullscreen'), + accelerator: process.platform === 'darwin' ? 'Cmd+Ctrl+F' : 'F11', + click(item, focusedWindow) { + if (focusedWindow) { + focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) + } + } + }, + { + type: 'separator' + }, + { + label: i18n.__('multiuserbrowser'), + accelerator: 'CmdOrCtrl+M', + type: 'checkbox', + checked: true, + click(item, focusedWindow) { + if (focusedWindow) { + main.store.set('multiUserBrowser', item.checked) + global.multiUserMode = item.checked + } + } + }, + { + label: i18n.__('minimalpad'), + accelerator: 'CmdOrCtrl+p', + type: 'checkbox', + checked: true, + click(item, focusedWindow) { + if (focusedWindow) { + main.store.set('minimalPad', item.checked) + global.useMinimalPad = item.checked + } + } + } + ] + }, + { + label: i18n.__('history'), + submenu: [ + { + id: 'history-back', + label: i18n.__('back'), + accelerator: 'CmdOrCtrl+Left', + click(item, focusedWindow) { + main.win.webContents.goBack() + } + }, + { + id: 'history-forward', + label: i18n.__('forward'), + accelerator: 'CmdOrCtrl+Right', + click(item, focusedWindow) { + main.win.webContents.goForward() + } + }, + { + label: i18n.__('home'), + accelerator: 'CmdOrCtrl+Up', + click(item, focusedWindow) { + main.win.webContents.goToIndex(0) + } + }, + { + type: 'separator' + }, + { + label: i18n.__('recentlyvisited'), + enabled: false + } + ] + }, + { + label: i18n.__('bookmarks'), + submenu: [ + { + label: i18n.__('localfilesystem'), + class: 'bookmark', + type: 'checkbox', + url: `file://${__dirname}/../index.html`, + accelerator: 'CmdOrCtrl+L', + click(item, focusedWindow) { + selectURL(item.url) + toggleBookmarks(item) + } + }, + { + label: i18n.__('testframes'), + class: 'bookmark', + type: 'checkbox', + url: `file://${__dirname}/../index.html?test`, + accelerator: 'CmdOrCtrl+T', + click(item, focusedWindow) { + selectURL(item.url) + toggleBookmarks(item) + } + }, + { + type: 'separator' + }, + { + id: 'localhost', + label: 'https://localhost:8443', + class: 'bookmark', + type: 'checkbox', + enabled: false, + url: 'https://localhost:8443/index.html', + click(item, focusedWindow) { + selectURL(item.url) + toggleBookmarks(item) + } + }, + { + id: 'localhost', + label: 'https://localhost:3000', + class: 'bookmark', + type: 'checkbox', + enabled: true, + url: 'https://localhost:3000/index.html', + click(item, focusedWindow) { + selectURL(item.url) + toggleBookmarks(item) + } + }, + // { + // label: 'http://tornado.iwm-kmrc.de:8000', + // class: 'bookmark', + // type: 'checkbox', + // url: 'http://tornado.iwm-kmrc.de:8000/index.html', + // click(item, focusedWindow) { + // selectURL(item.url) + // toggleBookmarks(item) + // } + // }, + { + label: 'http://rousseau.iwm-kmrc.de/index.html', + class: 'bookmark', + type: 'checkbox', + url: 'http://rousseau.iwm-kmrc.de/index.html', + click(item, focusedWindow) { + selectURL(item.url) + toggleBookmarks(item) + } + } + ] + }, + { + label: i18n.__('develop'), + submenu: [ + { + id: 'toggledevelopertools', + label: i18n.__('toggledevelopertools'), + accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', + click(item, focusedWindow) { + if (focusedWindow) focusedWindow.webContents.toggleDevTools() + } + }, + { + label: i18n.__('openprocessmonitor'), + accelerator: process.platform === 'darwin' ? 'Alt+Command+P' : 'Ctrl+Shift+P', + click(item, focusedWindow) { + openProcessManager() + } + }, + { + type: 'separator' + }, + { + label: i18n.__('selectfolder'), + accelerator: process.platform === 'darwin' ? 'Alt+Command+D' : 'Ctrl+Shift+D', + click(item, focusedWindow) { + if (process.platform === 'win32') { + var exec = require('child_process').exec + exec('NET SESSION', function (err, so, se) { + const admin = se.length === 0 ? true : false + + if (admin) { + showFolderBrowser(focusedWindow) + } else { + dialog.showMessageBox({ + type: 'error', + icon: path.join(__dirname, '../assets/icons/png/512x512-empty.png'), + buttons: [i18n.__('selectfolder.noadmin.ok')], + message: i18n.__('selectfolder.noadmin.message'), + detail: i18n.__('selectfolder.noadmin.detail') + }) + } + }) + } else { + showFolderBrowser(focusedWindow) + } + } + }, + { + type: 'separator' + }, + { + id: 'startserver', + label: i18n.__('startserver'), + accelerator: process.platform === 'darwin' ? 'Alt+Command+S' : 'Ctrl+Shift+S', + click(item, focusedWindow) { + const { server } = require('../server/main.js') + server.start() + item.visible = false + findItem('id', 'stopserver').visible = true + findItem('id', 'localhost').enabled = true + } + }, + { + id: 'stopserver', + label: i18n.__('stopserver'), + accelerator: process.platform === 'darwin' ? 'Alt+Command+S' : 'Ctrl+Shift+S', + visible: false, + click(item, focusedWindow) { + const { server } = require('../server/main.js') + server.stop() + item.visible = false + findItem('id', 'startserver').visible = true + findItem('id', 'localhost').enabled = false + } + }, + { + type: 'separator' + }, + { + label: i18n.__('runloadtests'), + accelerator: process.platform === 'darwin' ? 'Alt+Command+L' : 'Ctrl+Shift+L', + click(item, focusedWindow) { + loadTests(focusedWindow) + } + }, + { + type: 'separator' + }, + { + label: 'Aktualisiere Tüsch POIs', + click(item, focusedWindow) { + const UpdatePOI = require('../dev/tuesch/bin/menu/update-pois.js') + UpdatePOI.update('./dev/tuesch') + } + } + ] + }, + { + role: 'window', + label: i18n.__('window'), + submenu: [ + { + role: 'close', + label: i18n.__('close') + }, + { + role: 'minimize', + label: i18n.__('minimize') + }, + { + role: 'zoom', + label: i18n.__('zoom') + }, + { + type: 'separator' + }, + { + role: 'front', + label: i18n.__('front') + }, + { + type: 'separator' + }, + { + label: i18n.__('screenshot'), + accelerator: 'CmdOrCtrl+S', + async click(item, focusedWindow) { + if (focusedWindow) { + await focusedWindow.webContents.capturePage().then((image) => { + let screenshotFile = path.join(os.tmpdir(), 'screenshot.png') + + console.log('image captured', screenshotFile) + + let url = focusedWindow.webContents.getURL() + if (url.startsWith('file://')) { + let normalized = path.normalize(url).replace('.html', '.png') + screenshotFile = normalized.replace('file:', '') + let thumbnailFile = screenshotFile.replace('index.png', 'thumbnail.png') + if (url.endsWith('index.html')) { + thumbnailFile = screenshotFile.replace('index.png', 'thumbnail.png') + } else { + let folderName = path.dirname(screenshotFile) + let baseName = path.basename(screenshotFile) + thumbnailFile = path.join(folderName, 'thumbnails', baseName) + } + fs.writeFile(thumbnailFile, thumbnail(image), (err) => { + if (err) { + throw err + } else { + console.log(`Thumbnail written to ${thumbnailFile}`) + } + }) + } + fs.writeFile(screenshotFile, image.toPNG(), (err) => { + if (err) { + throw err + } else { + console.log(`Screenshot written to ${screenshotFile}`) + } + }) + }) + } + } + }, + { + type: 'separator' + } + ] + }, + { + role: 'help', + label: i18n.__('help'), + submenu: [ + { + label: i18n.__('iwm'), + click() { + shell.openExternal('https://www.iwm-tuebingen.de') + } + } + ] + } +] + +if (process.platform === 'darwin') { + const name = app.getName() + template.unshift({ + label: name, + submenu: [ + { + role: 'about', + label: i18n.__('about') + }, + { + type: 'separator' + }, + { + role: 'quit', + label: i18n.__('quit') + } + ] + }) +} + +const menu = Menu.buildFromTemplate(template) +Menu.setApplicationMenu(menu) + +checkBookmark(main.store.get('url')) +setHistoryStatus() + +function focus() { + findItem('id', 'forcereload').enabled = true + findItem('id', 'togglefullscreen').enabled = true + findItem('id', 'toggledevelopertools').enabled = true +} + +function blur() { + findItem('id', 'forcereload').enabled = false + findItem('id', 'togglefullscreen').enabled = false + findItem('id', 'toggledevelopertools').enabled = false +} + +module.exports = { + menu, + setHistoryStatus, + focus, + blur +} diff --git a/dist/iwmlib.js b/dist/iwmlib.js index 5bbdcc8..19afd00 100644 --- a/dist/iwmlib.js +++ b/dist/iwmlib.js @@ -3213,6 +3213,40 @@ * * @constructor */ + + + class RequestFrameThrower { + /** Implemenents the standard throw behavior. */ + animateThrow(throwable) { + /*** Calls the animateThrow method in sync with the display link. */ + requestAnimationFrame(throwable.animateThrow.bind(throwable)); + } + } + + class TimeoutThrower { + + constructor(delay=20) { + this.throwables = new Set(); + this.delay = delay; + setTimeout(this.animateStep.bind(this), this.delay); + } + + animateThrow(throwable) { + this.throwables.add(throwable); + } + + animateStep() { + let active = [...this.throwables]; + this.throwables.clear(); + for(let throwable of active) { + throwable.animateThrow(); + } + setTimeout(this.animateStep.bind(this), this.delay); + } + } + + let thrower = new RequestFrameThrower(); + class Throwable { constructor({ movableX = true, @@ -3220,7 +3254,7 @@ throwVisibility = 44, throwDamping = 0.95, autoThrow = true, - onThrowFinished = null, + onThrowFinished = null } = {}) { this.movableX = movableX; this.movableY = movableY; @@ -3229,9 +3263,16 @@ this.autoThrow = autoThrow; this.velocities = []; this.velocity = null; - this.timestamp = null; + this.lastframe = null; this.onThrowFinished = onThrowFinished; - //console.log("onThrowFinished", onThrowFinished) + } + + static defaultThrow() { + thrower = new RequestFrameThrower(); + } + + static timeoutThrow() { + thrower = new TimeoutThrower(); } observeVelocity() { @@ -3253,7 +3294,7 @@ t: t, dt: dt, dx: delta.x / number, - dy: delta.y / number, + dy: delta.y / number }; this.velocities.push(velocity); while (this.velocities.length > buffer) { @@ -3262,8 +3303,23 @@ } } + addTestVelocity(delta, dt=20, buffer = 5) { + let t = performance.now(); + this.lastframe = t; + let velocity = { + t: t, + dt: dt, + dx: delta.x , + dy: delta.y + }; + for(let i=0; i prevLength) { let factor = nextLength / prevLength; next = Points$1.multiplyScalar(next, 1 / factor); - console.log('Prevent acceleration', factor, this.velocity, next); + // console.log('Prevent acceleration', factor, this.velocity, next) } this.velocity = next; let d = Points$1.multiplyScalar(this.velocity, dt); @@ -3323,11 +3384,11 @@ this.onDragUpdate(d); if (dt == 0 || this.needsAnimation()) { - requestAnimationFrame(this.animateThrow.bind(this)); + thrower.animateThrow(this); return } else { if (this.isOutside()) { - requestAnimationFrame(this.animateThrow.bind(this)); + thrower.animateThrow(this); return } } @@ -3351,7 +3412,7 @@ let next = Points$1.multiplyScalar(velocity, this.throwDamping); return { x: this.movableX ? next.x : 0, - y: this.movableY ? next.y : 0, + y: this.movableY ? next.y : 0 } } @@ -3396,7 +3457,7 @@ scaleCloseBuffer = 0.05, maxRotation = Angle.degree2radian(5), minInteractionDistance = 0, - useLowPassFilter = false, + useLowPassFilter = false } = {}) { if (rotationDegrees != null && rotation != null) { throw new Error('Use rotationDegrees or rotation but not both') @@ -3411,7 +3472,7 @@ throwVisibility, throwDamping, autoThrow, - onThrowFinished, + onThrowFinished }); /** @@ -3486,7 +3547,7 @@ _callCloseCallbacks() { if (this.onClose) { - this.onClose.forEach((callback) => callback(this)); + this.onClose.forEach(callback => callback(this)); } } @@ -3506,6 +3567,9 @@ let delta = interaction.delta(); if (delta != null) { + /* uo: Is this the best place to add velocity? It works with scrollable text in card drawers but + has to be tested */ + this.addVelocity(delta); let rotate = delta.rotate; let zoom = delta.zoom; if (this.maxRotation != null) { @@ -3524,9 +3588,10 @@ zoomDelta *= ratio; zoom = 1 + zoomDelta; } - this.transform(delta, zoom, rotate, delta.about); - this.addVelocity(delta); // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855 + /* uo: This is too late an dangerous. transform sometimes modifies delta + this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855 + */ if (zoom != 1) this.interactionAnchor = delta.about; } @@ -3637,11 +3702,11 @@ if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) { this.zoom(this.minScale, { animate: 0.2, - onComplete: this.close.bind(this), + onComplete: this.close.bind(this) }); } else if (this.scale < this.minScale + this.scaleCloseThreshold) { this.zoom(this.minScale + this.scaleCloseThreshold, { - animate: 0.4, + animate: 0.4 }); } } @@ -3663,12 +3728,12 @@ x: '+=' + d.x, y: '+=' + d.y, /* scale: scale, uo: not defined, why was this here? */ - onUpdate: (e) => { + onUpdate: e => { let p = this.position; let dx = p.x - startPos.x; let dy = p.x - startPos.y; this.onMoved(dx, dy); - }, + } }); } else { this._move(d); @@ -3697,7 +3762,7 @@ scale: scale, delay: delay, onComplete: onComplete, - onUpdate: this.onZoomed.bind(this), + onUpdate: this.onZoomed.bind(this) }); } else { this.scale = scale; @@ -3715,7 +3780,7 @@ transform(translate, zoom, rotate, anchor) { let delta = { x: this.movableX ? translate.x : 0, - y: this.movableY ? translate.y : 0, + y: this.movableY ? translate.y : 0 }; if (this.resizable) var vzoom = zoom; if (!this.translatable) delta = { x: 0, y: 0 }; @@ -3730,9 +3795,9 @@ rotate: 0, about: anchor, fast: false, - type: UPDATE, + type: UPDATE }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -3761,9 +3826,9 @@ translate: delta, scale: newScale, rotate: rotate, - about: anchor, + about: anchor }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -3829,7 +3894,7 @@ if (this.scale > this.maxScale) zoom = 1 - amount; if (zoom != 1) { this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor); - requestAnimationFrame((dt) => { + requestAnimationFrame(dt => { this.animateZoomBounce(dt); }); return @@ -3886,9 +3951,9 @@ rotate: 0, about: null, fast: false, - type: START, + type: START }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -3904,13 +3969,13 @@ } onEnd(event, interaction) { - console.log('Scatter.onEnd', this.dragging); + //console.log('Scatter.onEnd', this.dragging) if (interaction.isFinished()) { this.endGesture(interaction); this.dragging = false; for (let key of interaction.ended.keys()) { if (interaction.isTap(key)) { - console.log('Scatter.isTap'); + //console.log('Scatter.isTap') let point = interaction.ended.get(key); this.onTap(event, interaction, point); } @@ -3922,9 +3987,9 @@ rotate: 0, about: null, fast: false, - type: END, + type: END }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -3938,7 +4003,7 @@ //onTap(event, interaction, point) {} onTap(event, interaction, point) { - console.log('AbstractScatter.onTap', this.tapDelegate, interaction); + //console.log('AbstractScatter.onTap', this.tapDelegate, interaction) if (this.tapDelegate) { Events.stop(event); this.tapDelegate.tap(event, 'scatter'); @@ -3952,9 +4017,9 @@ translate: delta, scale: this.scale, about: this.currentAbout, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -3966,9 +4031,9 @@ scale: this.scale, about: this.currentAbout, fast: false, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -3980,9 +4045,9 @@ translate: { x: dx, y: dy }, about: about, fast: true, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -3993,9 +4058,9 @@ let event = new ScatterEvent(this, { scale: this.scale, fast: false, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -4009,9 +4074,9 @@ scale: this.scale, about: about, fast: false, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -4043,7 +4108,7 @@ useCapture = true, capturePointerEvents = true, touchAction = 'none', - debugCanvas = null, + debugCanvas = null } = {} ) { this.onCapture = null; @@ -4055,7 +4120,7 @@ movement of scatter objects, the touchmove event has to be bound again. */ if (Capabilities.isSafari) { - document.addEventListener('touchmove', (event) => this.preventPinch(event), false); + document.addEventListener('touchmove', event => this.preventPinch(event), false); stopEvents = false; } else { stopEvents = true; @@ -4070,11 +4135,11 @@ this.delegate = new InteractionMapper$1(element, this, { useCapture, capturePointerEvents, - mouseWheelElement: window, + mouseWheelElement: window }); if (debugCanvas !== null) { - requestAnimationFrame((dt) => { + requestAnimationFrame(dt => { this.showTouches(dt, debugCanvas); }); } @@ -4096,7 +4161,7 @@ context.fill(); context.stroke(); } - requestAnimationFrame((dt) => { + requestAnimationFrame(dt => { this.showTouches(dt, canvas); }); } @@ -4238,7 +4303,7 @@ scaleCloseBuffer = 0.05, useLowPassFilter = false, maxRotation = Angle.degree2radian(15), - minInteractionDistance = 200, + minInteractionDistance = 200 } = {} ) { super({ @@ -4265,7 +4330,7 @@ onClose, useLowPassFilter, maxRotation, - minInteractionDistance, + minInteractionDistance }); if (container == null || width == null || height == null) { throw new Error('Invalid value: null') @@ -4293,7 +4358,7 @@ height: height, scale: startScale, rotation: this.startRotationDegrees, - transformOrigin: transformOrigin, + transformOrigin: transformOrigin }; this.tapNodes = new Map(); @@ -4315,15 +4380,15 @@ button.className = 'interactiveElement'; this.element.appendChild(button); - button.addEventListener('pointerdown', (e) => { + button.addEventListener('pointerdown', e => { this.startResize(e); }); - button.addEventListener('pointermove', (e) => { + button.addEventListener('pointermove', e => { this.resize(e); }); - button.addEventListener('pointerup', (e) => { + button.addEventListener('pointerup', e => { this.stopResize(e); }); this.resizeButton = button; @@ -4340,7 +4405,7 @@ scale: this.scale, x: this.x, y: this.y, - rotation: this.rotation, + rotation: this.rotation } } @@ -4391,7 +4456,7 @@ top: rect.top - stage.top, left: rect.left - stage.left, width: rect.width, - height: rect.height, + height: rect.height } } @@ -4432,7 +4497,7 @@ set scale(scale) { TweenLite.set(this.element, { scale: scale, - transformOrigin: this.transformOrigin, + transformOrigin: this.transformOrigin }); this._scale = scale; } @@ -4464,9 +4529,9 @@ hide() { TweenLite.to(this.element, 0.1, { display: 'none', - onComplete: (e) => { + onComplete: e => { this.element.parentNode.removeChild(this.element); - }, + } }); } @@ -4480,7 +4545,7 @@ x: p.x, y: p.y, rotation: rotationDegrees, - transformOrigin: this.transformOrigin, + transformOrigin: this.transformOrigin }); } @@ -4541,7 +4606,7 @@ let oldPostition = { x: this.element.getBoundingClientRect().left, - y: this.element.getBoundingClientRect().top, + y: this.element.getBoundingClientRect().top }; this.bringToFront(); @@ -4549,7 +4614,7 @@ let newPostition = { x: this.element.getBoundingClientRect().left, - y: this.element.getBoundingClientRect().top, + y: this.element.getBoundingClientRect().top }; let offset = Points$1.subtract(oldPostition, newPostition); @@ -4594,7 +4659,7 @@ ) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, - height: this.element.offsetHeight + resizeH / this.scale, + height: this.element.offsetHeight + resizeH / this.scale }); this.oldX = e.clientX; @@ -4611,12 +4676,12 @@ let event = new CustomEvent('resizeEnded'); let oldPostition = { x: this.element.getBoundingClientRect().left, - y: this.element.getBoundingClientRect().top, + y: this.element.getBoundingClientRect().top }; this.element.style.transformOrigin = '50% 50%'; let newPostition = { x: this.element.getBoundingClientRect().left, - y: this.element.getBoundingClientRect().top, + y: this.element.getBoundingClientRect().top }; let offset = Points$1.subtract(oldPostition, newPostition); @@ -4789,7 +4854,6 @@ tap(event, calledBy = 'unknown') { if (event.isTrusted) { let node = this.nearestActive(event); - console.log('tap', node); this.nodeTapped(node, event); /* let node = document.elementFromPoint(event.clientX, event.clientY) @@ -8195,7 +8259,6 @@ image, }); let center = this._calculateCenterRelativeTo(target, image); - console.log('_calculateCenterRelativeTo', center); TweenLite.set(maskImage, { transformOrigin: `${center.x} ${center.y}`, }); @@ -9099,7 +9162,6 @@ } static _calculateCenterRelativeTo(target, image) { - // console.log('_calculateCenterRelativeTo', target, image) let bbox = image.getBBox(); let width = bbox.width; let height = bbox.height; diff --git a/dist/iwmlib.pixi.js b/dist/iwmlib.pixi.js index 775a98b..ca5c18d 100644 --- a/dist/iwmlib.pixi.js +++ b/dist/iwmlib.pixi.js @@ -6778,6 +6778,40 @@ * * @constructor */ + + + class RequestFrameThrower { + /** Implemenents the standard throw behavior. */ + animateThrow(throwable) { + /*** Calls the animateThrow method in sync with the display link. */ + requestAnimationFrame(throwable.animateThrow.bind(throwable)); + } + } + + class TimeoutThrower { + + constructor(delay=20) { + this.throwables = new Set(); + this.delay = delay; + setTimeout(this.animateStep.bind(this), this.delay); + } + + animateThrow(throwable) { + this.throwables.add(throwable); + } + + animateStep() { + let active = [...this.throwables]; + this.throwables.clear(); + for(let throwable of active) { + throwable.animateThrow(); + } + setTimeout(this.animateStep.bind(this), this.delay); + } + } + + let thrower = new RequestFrameThrower(); + class Throwable { constructor({ movableX = true, @@ -6785,7 +6819,7 @@ throwVisibility = 44, throwDamping = 0.95, autoThrow = true, - onThrowFinished = null, + onThrowFinished = null } = {}) { this.movableX = movableX; this.movableY = movableY; @@ -6794,9 +6828,16 @@ this.autoThrow = autoThrow; this.velocities = []; this.velocity = null; - this.timestamp = null; + this.lastframe = null; this.onThrowFinished = onThrowFinished; - //console.log("onThrowFinished", onThrowFinished) + } + + static defaultThrow() { + thrower = new RequestFrameThrower(); + } + + static timeoutThrow() { + thrower = new TimeoutThrower(); } observeVelocity() { @@ -6818,7 +6859,7 @@ t: t, dt: dt, dx: delta.x / number, - dy: delta.y / number, + dy: delta.y / number }; this.velocities.push(velocity); while (this.velocities.length > buffer) { @@ -6827,8 +6868,23 @@ } } + addTestVelocity(delta, dt=20, buffer = 5) { + let t = performance.now(); + this.lastframe = t; + let velocity = { + t: t, + dt: dt, + dx: delta.x , + dy: delta.y + }; + for(let i=0; i prevLength) { let factor = nextLength / prevLength; next = Points.multiplyScalar(next, 1 / factor); - console.log('Prevent acceleration', factor, this.velocity, next); + // console.log('Prevent acceleration', factor, this.velocity, next) } this.velocity = next; let d = Points.multiplyScalar(this.velocity, dt); @@ -6888,11 +6949,11 @@ this.onDragUpdate(d); if (dt == 0 || this.needsAnimation()) { - requestAnimationFrame(this.animateThrow.bind(this)); + thrower.animateThrow(this); return } else { if (this.isOutside()) { - requestAnimationFrame(this.animateThrow.bind(this)); + thrower.animateThrow(this); return } } @@ -6916,7 +6977,7 @@ let next = Points.multiplyScalar(velocity, this.throwDamping); return { x: this.movableX ? next.x : 0, - y: this.movableY ? next.y : 0, + y: this.movableY ? next.y : 0 } } @@ -6961,7 +7022,7 @@ scaleCloseBuffer = 0.05, maxRotation = Angle.degree2radian(5), minInteractionDistance = 0, - useLowPassFilter = false, + useLowPassFilter = false } = {}) { if (rotationDegrees != null && rotation != null) { throw new Error('Use rotationDegrees or rotation but not both') @@ -6976,7 +7037,7 @@ throwVisibility, throwDamping, autoThrow, - onThrowFinished, + onThrowFinished }); /** @@ -7051,7 +7112,7 @@ _callCloseCallbacks() { if (this.onClose) { - this.onClose.forEach((callback) => callback(this)); + this.onClose.forEach(callback => callback(this)); } } @@ -7071,6 +7132,9 @@ let delta = interaction.delta(); if (delta != null) { + /* uo: Is this the best place to add velocity? It works with scrollable text in card drawers but + has to be tested */ + this.addVelocity(delta); let rotate = delta.rotate; let zoom = delta.zoom; if (this.maxRotation != null) { @@ -7089,9 +7153,10 @@ zoomDelta *= ratio; zoom = 1 + zoomDelta; } - this.transform(delta, zoom, rotate, delta.about); - this.addVelocity(delta); // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855 + /* uo: This is too late an dangerous. transform sometimes modifies delta + this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855 + */ if (zoom != 1) this.interactionAnchor = delta.about; } @@ -7202,11 +7267,11 @@ if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) { this.zoom(this.minScale, { animate: 0.2, - onComplete: this.close.bind(this), + onComplete: this.close.bind(this) }); } else if (this.scale < this.minScale + this.scaleCloseThreshold) { this.zoom(this.minScale + this.scaleCloseThreshold, { - animate: 0.4, + animate: 0.4 }); } } @@ -7228,12 +7293,12 @@ x: '+=' + d.x, y: '+=' + d.y, /* scale: scale, uo: not defined, why was this here? */ - onUpdate: (e) => { + onUpdate: e => { let p = this.position; let dx = p.x - startPos.x; let dy = p.x - startPos.y; this.onMoved(dx, dy); - }, + } }); } else { this._move(d); @@ -7262,7 +7327,7 @@ scale: scale, delay: delay, onComplete: onComplete, - onUpdate: this.onZoomed.bind(this), + onUpdate: this.onZoomed.bind(this) }); } else { this.scale = scale; @@ -7280,7 +7345,7 @@ transform(translate, zoom, rotate, anchor) { let delta = { x: this.movableX ? translate.x : 0, - y: this.movableY ? translate.y : 0, + y: this.movableY ? translate.y : 0 }; if (this.resizable) var vzoom = zoom; if (!this.translatable) delta = { x: 0, y: 0 }; @@ -7295,9 +7360,9 @@ rotate: 0, about: anchor, fast: false, - type: UPDATE, + type: UPDATE }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -7326,9 +7391,9 @@ translate: delta, scale: newScale, rotate: rotate, - about: anchor, + about: anchor }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -7394,7 +7459,7 @@ if (this.scale > this.maxScale) zoom = 1 - amount; if (zoom != 1) { this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor); - requestAnimationFrame((dt) => { + requestAnimationFrame(dt => { this.animateZoomBounce(dt); }); return @@ -7451,9 +7516,9 @@ rotate: 0, about: null, fast: false, - type: START, + type: START }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -7469,13 +7534,13 @@ } onEnd(event, interaction) { - console.log('Scatter.onEnd', this.dragging); + //console.log('Scatter.onEnd', this.dragging) if (interaction.isFinished()) { this.endGesture(interaction); this.dragging = false; for (let key of interaction.ended.keys()) { if (interaction.isTap(key)) { - console.log('Scatter.isTap'); + //console.log('Scatter.isTap') let point = interaction.ended.get(key); this.onTap(event, interaction, point); } @@ -7487,9 +7552,9 @@ rotate: 0, about: null, fast: false, - type: END, + type: END }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -7503,7 +7568,7 @@ //onTap(event, interaction, point) {} onTap(event, interaction, point) { - console.log('AbstractScatter.onTap', this.tapDelegate, interaction); + //console.log('AbstractScatter.onTap', this.tapDelegate, interaction) if (this.tapDelegate) { Events$1.stop(event); this.tapDelegate.tap(event, 'scatter'); @@ -7517,9 +7582,9 @@ translate: delta, scale: this.scale, about: this.currentAbout, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -7531,9 +7596,9 @@ scale: this.scale, about: this.currentAbout, fast: false, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -7545,9 +7610,9 @@ translate: { x: dx, y: dy }, about: about, fast: true, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -7558,9 +7623,9 @@ let event = new ScatterEvent(this, { scale: this.scale, fast: false, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -7574,9 +7639,9 @@ scale: this.scale, about: about, fast: false, - type: null, + type: null }); - this.onTransform.forEach(function (f) { + this.onTransform.forEach(function(f) { f(event); }); } @@ -7623,7 +7688,7 @@ scaleCloseBuffer = 0.05, useLowPassFilter = false, maxRotation = Angle.degree2radian(15), - minInteractionDistance = 200, + minInteractionDistance = 200 } = {} ) { super({ @@ -7650,7 +7715,7 @@ onClose, useLowPassFilter, maxRotation, - minInteractionDistance, + minInteractionDistance }); if (container == null || width == null || height == null) { throw new Error('Invalid value: null') @@ -7678,7 +7743,7 @@ height: height, scale: startScale, rotation: this.startRotationDegrees, - transformOrigin: transformOrigin, + transformOrigin: transformOrigin }; this.tapNodes = new Map(); @@ -7700,15 +7765,15 @@ button.className = 'interactiveElement'; this.element.appendChild(button); - button.addEventListener('pointerdown', (e) => { + button.addEventListener('pointerdown', e => { this.startResize(e); }); - button.addEventListener('pointermove', (e) => { + button.addEventListener('pointermove', e => { this.resize(e); }); - button.addEventListener('pointerup', (e) => { + button.addEventListener('pointerup', e => { this.stopResize(e); }); this.resizeButton = button; @@ -7725,7 +7790,7 @@ scale: this.scale, x: this.x, y: this.y, - rotation: this.rotation, + rotation: this.rotation } } @@ -7776,7 +7841,7 @@ top: rect.top - stage.top, left: rect.left - stage.left, width: rect.width, - height: rect.height, + height: rect.height } } @@ -7817,7 +7882,7 @@ set scale(scale) { TweenLite.set(this.element, { scale: scale, - transformOrigin: this.transformOrigin, + transformOrigin: this.transformOrigin }); this._scale = scale; } @@ -7849,9 +7914,9 @@ hide() { TweenLite.to(this.element, 0.1, { display: 'none', - onComplete: (e) => { + onComplete: e => { this.element.parentNode.removeChild(this.element); - }, + } }); } @@ -7865,7 +7930,7 @@ x: p.x, y: p.y, rotation: rotationDegrees, - transformOrigin: this.transformOrigin, + transformOrigin: this.transformOrigin }); } @@ -7926,7 +7991,7 @@ let oldPostition = { x: this.element.getBoundingClientRect().left, - y: this.element.getBoundingClientRect().top, + y: this.element.getBoundingClientRect().top }; this.bringToFront(); @@ -7934,7 +7999,7 @@ let newPostition = { x: this.element.getBoundingClientRect().left, - y: this.element.getBoundingClientRect().top, + y: this.element.getBoundingClientRect().top }; let offset = Points.subtract(oldPostition, newPostition); @@ -7979,7 +8044,7 @@ ) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, - height: this.element.offsetHeight + resizeH / this.scale, + height: this.element.offsetHeight + resizeH / this.scale }); this.oldX = e.clientX; @@ -7996,12 +8061,12 @@ let event = new CustomEvent('resizeEnded'); let oldPostition = { x: this.element.getBoundingClientRect().left, - y: this.element.getBoundingClientRect().top, + y: this.element.getBoundingClientRect().top }; this.element.style.transformOrigin = '50% 50%'; let newPostition = { x: this.element.getBoundingClientRect().left, - y: this.element.getBoundingClientRect().top, + y: this.element.getBoundingClientRect().top }; let offset = Points.subtract(oldPostition, newPostition); @@ -8174,7 +8239,6 @@ tap(event, calledBy = 'unknown') { if (event.isTrusted) { let node = this.nearestActive(event); - console.log('tap', node); this.nodeTapped(node, event); /* let node = document.elementFromPoint(event.clientX, event.clientY) diff --git a/lib/_menu.js b/lib/_menu.js old mode 100755 new mode 100644 diff --git a/lib/card/card.js b/lib/card/card.js index f1d6504..01f48eb 100644 --- a/lib/card/card.js +++ b/lib/card/card.js @@ -766,7 +766,6 @@ export default class Card { } static _calculateCenterRelativeTo(target, image) { - // console.log('_calculateCenterRelativeTo', target, image) let bbox = image.getBBox() let width = bbox.width let height = bbox.height diff --git a/lib/card/highlight.js b/lib/card/highlight.js index 49cb59d..381f941 100644 --- a/lib/card/highlight.js +++ b/lib/card/highlight.js @@ -213,7 +213,6 @@ export default class Highlight extends Object { image, }) let center = this._calculateCenterRelativeTo(target, image) - console.log('_calculateCenterRelativeTo', center) TweenLite.set(maskImage, { transformOrigin: `${center.x} ${center.y}`, }) diff --git a/lib/card/wrapper.js b/lib/card/wrapper.js index 83d4ad9..ceabbac 100644 --- a/lib/card/wrapper.js +++ b/lib/card/wrapper.js @@ -158,7 +158,6 @@ export default class CardWrapper extends Object { tap(event, calledBy = 'unknown') { if (event.isTrusted) { let node = this.nearestActive(event) - console.log('tap', node) this.nodeTapped(node, event) /* let node = document.elementFromPoint(event.clientX, event.clientY) diff --git a/lib/flippable.html b/lib/flippable.html index 23f7aa4..e0dd6c3 100644 --- a/lib/flippable.html +++ b/lib/flippable.html @@ -17,44 +17,60 @@ - + - +
+
- - - -

lib.Flippable

-

- The flip effect simulates a flip between a front and back view of an object by means of a 3D rotation. The - DOMFlippable class implements this effect for two DOM nodes, one as the front view, the other as the back - view. Both views are connected via a HTML template that defines the placeholders for front and back views. - The style file "css/flipeffect.css" holds reasonable default styles for this kind of templates. -

-

+            
+        
+    
+
+
+
+

+ Flippable +

+

+The flip effect simulates a flip between a front and back view of an object +by means of a 3D rotation. The DOMFlippable class implements this effect for two +DOM nodes, one as the front view, the other as the back view. Both views are connected +via a HTML template that defines the placeholders for front and back views. The +style file "css/flipeffect.css" holds reasonable default styles for this kind of +templates. +

+

+The SVG buttons have to be wrapped in an HTML DOM element which handles events. Otherwise, +the viewbox of the SVG will interfere with the coordinate transformation. +

+

     <template id="flipTemplate">
         <div class="flipWrapper">
             <div class="flipCard">
                 <div class="flipFace front"></div>
                 <div class="flipFace back" style="visibility:hidden;"></div>
             </div>
-            <svg class="flipButton backBtn" .../>
-            <svg class="flipButton infoBtn" .../>
+            <div class="flipButton backBtn" .../>
+                <svg .../>
+            </div>
+            <div class="flipButton infoBtn" .../>
+                <svg .../>
+            </div>
         </div>
     </template>
 
diff --git a/lib/scatter.js b/lib/scatter.js
index c75a3d7..7066dcb 100644
--- a/lib/scatter.js
+++ b/lib/scatter.js
@@ -88,6 +88,40 @@ export class ResizeEvent extends BaseEvent {
  *
  * @constructor
  */
+
+
+class RequestFrameThrower {
+    /** Implemenents the standard throw behavior. */
+    animateThrow(throwable) {
+        /*** Calls the animateThrow method in sync with the display link. */
+        requestAnimationFrame(throwable.animateThrow.bind(throwable))
+    }
+}
+
+class TimeoutThrower {
+
+    constructor(delay=20) {
+        this.throwables = new Set()
+        this.delay = delay
+        setTimeout(this.animateStep.bind(this), this.delay)
+    }
+
+    animateThrow(throwable) {
+        this.throwables.add(throwable)
+    }
+
+    animateStep() {
+        let active = [...this.throwables]
+        this.throwables.clear()
+        for(let throwable of active) {
+            throwable.animateThrow()
+        }
+        setTimeout(this.animateStep.bind(this), this.delay)
+    }
+}
+
+let thrower = new RequestFrameThrower()
+
 class Throwable {
     constructor({
         movableX = true,
@@ -95,7 +129,7 @@ class Throwable {
         throwVisibility = 44,
         throwDamping = 0.95,
         autoThrow = true,
-        onThrowFinished = null,
+        onThrowFinished = null
     } = {}) {
         this.movableX = movableX
         this.movableY = movableY
@@ -104,9 +138,16 @@ class Throwable {
         this.autoThrow = autoThrow
         this.velocities = []
         this.velocity = null
-        this.timestamp = null
+        this.lastframe = null
         this.onThrowFinished = onThrowFinished
-        //console.log("onThrowFinished", onThrowFinished)
+    }
+
+    static defaultThrow() {
+        thrower = new RequestFrameThrower()
+    }
+
+    static timeoutThrow() {
+        thrower = new TimeoutThrower()
     }
 
     observeVelocity() {
@@ -128,7 +169,7 @@ class Throwable {
                 t: t,
                 dt: dt,
                 dx: delta.x / number,
-                dy: delta.y / number,
+                dy: delta.y / number
             }
             this.velocities.push(velocity)
             while (this.velocities.length > buffer) {
@@ -137,8 +178,23 @@ class Throwable {
         }
     }
 
+    addTestVelocity(delta, dt=20, buffer = 5) {
+        let t = performance.now()
+        this.lastframe = t
+        let velocity = {
+            t: t,
+            dt: dt,
+            dx: delta.x ,
+            dy: delta.y 
+        }
+        for(let i=0; i prevLength) {
                 let factor = nextLength / prevLength
                 next = Points.multiplyScalar(next, 1 / factor)
-                console.log('Prevent acceleration', factor, this.velocity, next)
+                // console.log('Prevent acceleration', factor, this.velocity, next)
             }
             this.velocity = next
             let d = Points.multiplyScalar(this.velocity, dt)
@@ -198,11 +259,11 @@ class Throwable {
 
             this.onDragUpdate(d)
             if (dt == 0 || this.needsAnimation()) {
-                requestAnimationFrame(this.animateThrow.bind(this))
+                thrower.animateThrow(this)
                 return
             } else {
                 if (this.isOutside()) {
-                    requestAnimationFrame(this.animateThrow.bind(this))
+                    thrower.animateThrow(this)
                     return
                 }
             }
@@ -226,7 +287,7 @@ class Throwable {
         let next = Points.multiplyScalar(velocity, this.throwDamping)
         return {
             x: this.movableX ? next.x : 0,
-            y: this.movableY ? next.y : 0,
+            y: this.movableY ? next.y : 0
         }
     }
 
@@ -271,7 +332,7 @@ export class AbstractScatter extends Throwable {
         scaleCloseBuffer = 0.05,
         maxRotation = Angle.degree2radian(5),
         minInteractionDistance = 0,
-        useLowPassFilter = false,
+        useLowPassFilter = false
     } = {}) {
         if (rotationDegrees != null && rotation != null) {
             throw new Error('Use rotationDegrees or rotation but not both')
@@ -286,7 +347,7 @@ export class AbstractScatter extends Throwable {
             throwVisibility,
             throwDamping,
             autoThrow,
-            onThrowFinished,
+            onThrowFinished
         })
 
         /**
@@ -361,7 +422,7 @@ export class AbstractScatter extends Throwable {
 
     _callCloseCallbacks() {
         if (this.onClose) {
-            this.onClose.forEach((callback) => callback(this))
+            this.onClose.forEach(callback => callback(this))
         }
     }
 
@@ -381,6 +442,9 @@ export class AbstractScatter extends Throwable {
         let delta = interaction.delta()
 
         if (delta != null) {
+            /* uo: Is this the best place to add velocity? It works with scrollable text in card drawers but
+            has to be tested */
+            this.addVelocity(delta)
             let rotate = delta.rotate
             let zoom = delta.zoom
             if (this.maxRotation != null) {
@@ -399,9 +463,10 @@ export class AbstractScatter extends Throwable {
                 zoomDelta *= ratio
                 zoom = 1 + zoomDelta
             }
-
             this.transform(delta, zoom, rotate, delta.about)
+            /* uo: This is too late an dangerous. transform sometimes modifies delta
             this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
+            */
 
             if (zoom != 1) this.interactionAnchor = delta.about
         }
@@ -512,11 +577,11 @@ export class AbstractScatter extends Throwable {
             if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) {
                 this.zoom(this.minScale, {
                     animate: 0.2,
-                    onComplete: this.close.bind(this),
+                    onComplete: this.close.bind(this)
                 })
             } else if (this.scale < this.minScale + this.scaleCloseThreshold) {
                 this.zoom(this.minScale + this.scaleCloseThreshold, {
-                    animate: 0.4,
+                    animate: 0.4
                 })
             }
     }
@@ -538,12 +603,12 @@ export class AbstractScatter extends Throwable {
                     x: '+=' + d.x,
                     y: '+=' + d.y,
                     /* scale: scale, uo: not defined, why was this here? */
-                    onUpdate: (e) => {
+                    onUpdate: e => {
                         let p = this.position
                         let dx = p.x - startPos.x
                         let dy = p.x - startPos.y
                         this.onMoved(dx, dy)
-                    },
+                    }
                 })
             } else {
                 this._move(d)
@@ -572,7 +637,7 @@ export class AbstractScatter extends Throwable {
                     scale: scale,
                     delay: delay,
                     onComplete: onComplete,
-                    onUpdate: this.onZoomed.bind(this),
+                    onUpdate: this.onZoomed.bind(this)
                 })
             } else {
                 this.scale = scale
@@ -590,7 +655,7 @@ export class AbstractScatter extends Throwable {
     transform(translate, zoom, rotate, anchor) {
         let delta = {
             x: this.movableX ? translate.x : 0,
-            y: this.movableY ? translate.y : 0,
+            y: this.movableY ? translate.y : 0
         }
         if (this.resizable) var vzoom = zoom
         if (!this.translatable) delta = { x: 0, y: 0 }
@@ -605,9 +670,9 @@ export class AbstractScatter extends Throwable {
                     rotate: 0,
                     about: anchor,
                     fast: false,
-                    type: UPDATE,
+                    type: UPDATE
                 })
-                this.onTransform.forEach(function (f) {
+                this.onTransform.forEach(function(f) {
                     f(event)
                 })
             }
@@ -636,9 +701,9 @@ export class AbstractScatter extends Throwable {
                 translate: delta,
                 scale: newScale,
                 rotate: rotate,
-                about: anchor,
+                about: anchor
             })
-            this.onTransform.forEach(function (f) {
+            this.onTransform.forEach(function(f) {
                 f(event)
             })
         }
@@ -704,7 +769,7 @@ export class AbstractScatter extends Throwable {
             if (this.scale > this.maxScale) zoom = 1 - amount
             if (zoom != 1) {
                 this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor)
-                requestAnimationFrame((dt) => {
+                requestAnimationFrame(dt => {
                     this.animateZoomBounce(dt)
                 })
                 return
@@ -761,9 +826,9 @@ export class AbstractScatter extends Throwable {
                 rotate: 0,
                 about: null,
                 fast: false,
-                type: START,
+                type: START
             })
-            this.onTransform.forEach(function (f) {
+            this.onTransform.forEach(function(f) {
                 f(event)
             })
         }
@@ -779,13 +844,13 @@ export class AbstractScatter extends Throwable {
     }
 
     onEnd(event, interaction) {
-        console.log('Scatter.onEnd', this.dragging)
+        //console.log('Scatter.onEnd', this.dragging)
         if (interaction.isFinished()) {
             this.endGesture(interaction)
             this.dragging = false
             for (let key of interaction.ended.keys()) {
                 if (interaction.isTap(key)) {
-                    console.log('Scatter.isTap')
+                    //console.log('Scatter.isTap')
                     let point = interaction.ended.get(key)
                     this.onTap(event, interaction, point)
                 }
@@ -797,9 +862,9 @@ export class AbstractScatter extends Throwable {
                     rotate: 0,
                     about: null,
                     fast: false,
-                    type: END,
+                    type: END
                 })
-                this.onTransform.forEach(function (f) {
+                this.onTransform.forEach(function(f) {
                     f(event)
                 })
             }
@@ -813,7 +878,7 @@ export class AbstractScatter extends Throwable {
     //onTap(event, interaction, point) {}
 
     onTap(event, interaction, point) {
-        console.log('AbstractScatter.onTap', this.tapDelegate, interaction)
+        //console.log('AbstractScatter.onTap', this.tapDelegate, interaction)
         if (this.tapDelegate) {
             Events.stop(event)
             this.tapDelegate.tap(event, 'scatter')
@@ -827,9 +892,9 @@ export class AbstractScatter extends Throwable {
                 translate: delta,
                 scale: this.scale,
                 about: this.currentAbout,
-                type: null,
+                type: null
             })
-            this.onTransform.forEach(function (f) {
+            this.onTransform.forEach(function(f) {
                 f(event)
             })
         }
@@ -841,9 +906,9 @@ export class AbstractScatter extends Throwable {
                 scale: this.scale,
                 about: this.currentAbout,
                 fast: false,
-                type: null,
+                type: null
             })
-            this.onTransform.forEach(function (f) {
+            this.onTransform.forEach(function(f) {
                 f(event)
             })
         }
@@ -855,9 +920,9 @@ export class AbstractScatter extends Throwable {
                 translate: { x: dx, y: dy },
                 about: about,
                 fast: true,
-                type: null,
+                type: null
             })
-            this.onTransform.forEach(function (f) {
+            this.onTransform.forEach(function(f) {
                 f(event)
             })
         }
@@ -868,9 +933,9 @@ export class AbstractScatter extends Throwable {
             let event = new ScatterEvent(this, {
                 scale: this.scale,
                 fast: false,
-                type: null,
+                type: null
             })
-            this.onTransform.forEach(function (f) {
+            this.onTransform.forEach(function(f) {
                 f(event)
             })
         }
@@ -884,9 +949,9 @@ export class AbstractScatter extends Throwable {
                 scale: this.scale,
                 about: about,
                 fast: false,
-                type: null,
+                type: null
             })
-            this.onTransform.forEach(function (f) {
+            this.onTransform.forEach(function(f) {
                 f(event)
             })
         }
@@ -918,7 +983,7 @@ export class DOMScatterContainer {
             useCapture = true,
             capturePointerEvents = true,
             touchAction = 'none',
-            debugCanvas = null,
+            debugCanvas = null
         } = {}
     ) {
         this.onCapture = null
@@ -930,7 +995,7 @@ export class DOMScatterContainer {
             movement of scatter objects, the touchmove event has to be bound again.
             */
             if (Capabilities.isSafari) {
-                document.addEventListener('touchmove', (event) => this.preventPinch(event), false)
+                document.addEventListener('touchmove', event => this.preventPinch(event), false)
                 stopEvents = false
             } else {
                 stopEvents = true
@@ -945,11 +1010,11 @@ export class DOMScatterContainer {
         this.delegate = new InteractionMapper(element, this, {
             useCapture,
             capturePointerEvents,
-            mouseWheelElement: window,
+            mouseWheelElement: window
         })
 
         if (debugCanvas !== null) {
-            requestAnimationFrame((dt) => {
+            requestAnimationFrame(dt => {
                 this.showTouches(dt, debugCanvas)
             })
         }
@@ -971,7 +1036,7 @@ export class DOMScatterContainer {
             context.fill()
             context.stroke()
         }
-        requestAnimationFrame((dt) => {
+        requestAnimationFrame(dt => {
             this.showTouches(dt, canvas)
         })
     }
@@ -1113,7 +1178,7 @@ export class DOMScatter extends AbstractScatter {
             scaleCloseBuffer = 0.05,
             useLowPassFilter = false,
             maxRotation = Angle.degree2radian(15),
-            minInteractionDistance = 200,
+            minInteractionDistance = 200
         } = {}
     ) {
         super({
@@ -1140,7 +1205,7 @@ export class DOMScatter extends AbstractScatter {
             onClose,
             useLowPassFilter,
             maxRotation,
-            minInteractionDistance,
+            minInteractionDistance
         })
         if (container == null || width == null || height == null) {
             throw new Error('Invalid value: null')
@@ -1168,7 +1233,7 @@ export class DOMScatter extends AbstractScatter {
             height: height,
             scale: startScale,
             rotation: this.startRotationDegrees,
-            transformOrigin: transformOrigin,
+            transformOrigin: transformOrigin
         }
         this.tapNodes = new Map()
 
@@ -1190,15 +1255,15 @@ export class DOMScatter extends AbstractScatter {
             button.className = 'interactiveElement'
             this.element.appendChild(button)
 
-            button.addEventListener('pointerdown', (e) => {
+            button.addEventListener('pointerdown', e => {
                 this.startResize(e)
             })
 
-            button.addEventListener('pointermove', (e) => {
+            button.addEventListener('pointermove', e => {
                 this.resize(e)
             })
 
-            button.addEventListener('pointerup', (e) => {
+            button.addEventListener('pointerup', e => {
                 this.stopResize(e)
             })
             this.resizeButton = button
@@ -1215,7 +1280,7 @@ export class DOMScatter extends AbstractScatter {
             scale: this.scale,
             x: this.x,
             y: this.y,
-            rotation: this.rotation,
+            rotation: this.rotation
         }
     }
 
@@ -1266,7 +1331,7 @@ export class DOMScatter extends AbstractScatter {
             top: rect.top - stage.top,
             left: rect.left - stage.left,
             width: rect.width,
-            height: rect.height,
+            height: rect.height
         }
     }
 
@@ -1307,7 +1372,7 @@ export class DOMScatter extends AbstractScatter {
     set scale(scale) {
         TweenLite.set(this.element, {
             scale: scale,
-            transformOrigin: this.transformOrigin,
+            transformOrigin: this.transformOrigin
         })
         this._scale = scale
     }
@@ -1339,9 +1404,9 @@ export class DOMScatter extends AbstractScatter {
     hide() {
         TweenLite.to(this.element, 0.1, {
             display: 'none',
-            onComplete: (e) => {
+            onComplete: e => {
                 this.element.parentNode.removeChild(this.element)
-            },
+            }
         })
     }
 
@@ -1355,7 +1420,7 @@ export class DOMScatter extends AbstractScatter {
             x: p.x,
             y: p.y,
             rotation: rotationDegrees,
-            transformOrigin: this.transformOrigin,
+            transformOrigin: this.transformOrigin
         })
     }
 
@@ -1416,7 +1481,7 @@ export class DOMScatter extends AbstractScatter {
 
         let oldPostition = {
             x: this.element.getBoundingClientRect().left,
-            y: this.element.getBoundingClientRect().top,
+            y: this.element.getBoundingClientRect().top
         }
         this.bringToFront()
 
@@ -1424,7 +1489,7 @@ export class DOMScatter extends AbstractScatter {
 
         let newPostition = {
             x: this.element.getBoundingClientRect().left,
-            y: this.element.getBoundingClientRect().top,
+            y: this.element.getBoundingClientRect().top
         }
 
         let offset = Points.subtract(oldPostition, newPostition)
@@ -1469,7 +1534,7 @@ export class DOMScatter extends AbstractScatter {
             )
                 TweenLite.to(this.element, 0, {
                     width: this.element.offsetWidth + resizeW / this.scale,
-                    height: this.element.offsetHeight + resizeH / this.scale,
+                    height: this.element.offsetHeight + resizeH / this.scale
                 })
 
             this.oldX = e.clientX
@@ -1486,12 +1551,12 @@ export class DOMScatter extends AbstractScatter {
         let event = new CustomEvent('resizeEnded')
         let oldPostition = {
             x: this.element.getBoundingClientRect().left,
-            y: this.element.getBoundingClientRect().top,
+            y: this.element.getBoundingClientRect().top
         }
         this.element.style.transformOrigin = '50% 50%'
         let newPostition = {
             x: this.element.getBoundingClientRect().left,
-            y: this.element.getBoundingClientRect().top,
+            y: this.element.getBoundingClientRect().top
         }
         let offset = Points.subtract(oldPostition, newPostition)