diff --git a/bin/browser.sh b/bin/browser.sh
new file mode 100755
index 0000000..7fd9d1d
--- /dev/null
+++ b/bin/browser.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+if [ "$1" = "dist" ]
+then
+ if [ -z "$2" ]
+ then
+ npm run package-all
+ else
+ npm run package-app-$2
+ fi
+else
+ electron . ./lib/index.html
+fi
\ No newline at end of file
diff --git a/browser/browser.html b/browser/browser.html
new file mode 100644
index 0000000..e3bb99d
--- /dev/null
+++ b/browser/browser.html
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+ Browser
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/carlo.js b/browser/carlo.js
new file mode 100644
index 0000000..8cee63a
--- /dev/null
+++ b/browser/carlo.js
@@ -0,0 +1,49 @@
+const carlo = require('carlo');
+const fse = require('fs-extra');
+const urlExists = require('url-exists');
+
+// command line arguments
+let path = 'index.html'
+process.argv.forEach((value, index, array) => {
+ if (index === 2) {
+ path = value
+ }
+});
+
+(async () => {
+ // Launch the browser.
+ const opts = {}
+
+ // Set path to custom chrome
+ const chrome = `${__dirname}/../chrome/chrome.exe`
+ if (fse.pathExistsSync(chrome)) {
+ opts.executablePath = chrome
+ }
+
+ // Launch app
+ const app = await carlo.launch(opts)
+
+ // Terminate Node.js process on app window closing.
+ app.on('exit', () => process.exit())
+
+ // Tell carlo where your web files are located.
+ app.serveFolder(`${__dirname}/../`)
+
+ // Check if URL exists
+ urlExists('https://localhost:8443', async (error, exists) => {
+
+ if (exists) {
+ console.info('Serve files via server')
+ app.serveOrigin('https://localhost:8443') // Optional
+ } else {
+ console.info('Serve files from file system')
+ }
+
+ // Expose 'env' function in the web environment.
+ await app.exposeFunction('env', _ => process.env)
+
+ // Navigate to the main page of your app.
+ console.info('Starting carlo with', path)
+ await app.load(path)
+ })
+})()
diff --git a/browser/i18n.js b/browser/i18n.js
new file mode 100644
index 0000000..f262f8c
--- /dev/null
+++ b/browser/i18n.js
@@ -0,0 +1,24 @@
+const path = require('path')
+const electron = require('electron')
+const fs = require('fs')
+let loadedLanguage
+let app = electron.app ? electron.app : electron.remote.app
+
+module.exports = i18n
+
+function i18n() {
+ if (fs.existsSync(path.join(__dirname, 'i18n', app.getLocale() + '.js'))) {
+ loadedLanguage = JSON.parse(fs.readFileSync(path.join(__dirname, 'i18n', app.getLocale() + '.js'), 'utf8'))
+ }
+ else {
+ loadedLanguage = JSON.parse(fs.readFileSync(path.join(__dirname, 'i18n', 'en.js'), 'utf8'))
+ }
+}
+
+i18n.prototype.__ = function(phrase) {
+ let translation = loadedLanguage[phrase]
+ if (translation === undefined) {
+ translation = phrase
+ }
+ return translation
+}
\ No newline at end of file
diff --git a/browser/i18n/de.js b/browser/i18n/de.js
new file mode 100644
index 0000000..2af6e0c
--- /dev/null
+++ b/browser/i18n/de.js
@@ -0,0 +1,61 @@
+{
+ "edit": "Bearbeiten",
+ "undo": "Widerrufen",
+ "redo": "Wiederholen",
+ "cut": "Ausschneiden",
+ "copy": "Kopieren",
+ "paste": "Einsetzen",
+ "pasteandmatchstyle": "Einsetzen und Stil anpassen",
+ "delete": "Löschen",
+ "selectall": "Alles auswählen",
+ "view": "Darstellung",
+ "reload": "Seite neu laden",
+ "forcereload": "Cache löschen und Seite neu laden",
+ "resetzoom": "Originalgröße",
+ "zoomin": "Vergrößern",
+ "zoomout": "Verkleinern",
+ "togglefullscreen": "Vollbildmodus umschalten",
+ "minimalpad": "Minimal-Pad",
+ "multiuserbrowser": "Mehrbenutzer-Browser",
+ "history": "Verlauf",
+ "back": "Zurück",
+ "forward": "Vorwärts",
+ "home": "Startseite",
+ "recentlyvisited": "Kürzlich besucht",
+ "bookmarks": "Lesezeichen",
+ "localfilesystem": "Lokales Dateisystem",
+ "testframes": "Testseiten",
+ "develop": "Entwickler",
+ "toggledevelopertools": "Webinformationen umschalten",
+ "openprocessmonitor": "Prozessmonitor öffnen",
+ "selectfolder": "Datenverzeichnis auswählen...",
+ "selectfolder.noadmin.ok": "OK",
+ "selectfolder.noadmin.message": "Keine ausreichenden Berechtigungen",
+ "selectfolder.noadmin.detail": "Um das Datenverzeichnis zu ändern, muss der IWM Browser mit Administrator-Berechtigungen gestartet werden.",
+ "selectfolder.warning.next": "Weiter",
+ "selectfolder.warning.cancel": "Abbrechen",
+ "selectfolder.warning.message": "Datenverzeichnis vorhanden",
+ "selectfolder.warning.detail": "Ihr IWM Browser besitzt bereits ein (verlinktes) Datenverzeichnis. Wenn Sie fortfahren, wird das alte Verzeichnis gesichert und ein neues wird erstellt.",
+ "selectfolder.select.title": "Datenverzeichnis wählen",
+ "selectfolder.select.buttonLabel": "Auswählen",
+ "selectfolder.samefolder.ok": "OK",
+ "selectfolder.samefolder.message": "Ungültiges Datenverzeichnis",
+ "selectfolder.samefolder.detail.same": "Das alte Datenverzeichnis darf nicht als neues Verzeichnis ausgewählt werden.",
+ "selectfolder.samefolder.detail.within": "Das neue Datenverzeichnis darf sich nicht innerhalb des alten Verzeichnisses befinden.",
+ "selectfolder.info.ok": "OK",
+ "selectfolder.info.message": "Link auf Datenverzeichnis erstellt",
+ "selectfolder.info.detail": "Der IWM Browser verwendet nun den Ordner \"${0}\" als neues Datenverzeichnis.",
+ "startserver": "Starte Server",
+ "stopserver": "Stoppe Server",
+ "runloadtests": "Starte Ladetests",
+ "window": "Fenster",
+ "close": "Fenster schließen",
+ "minimize": "Im Dock ablegen",
+ "zoom": "Zoomen",
+ "front": "Alle nach vorne bringen",
+ "screenshot": "Bildschirmfoto erstellen",
+ "help": "Hilfe",
+ "iwm": "Leibniz-Institut für Wissensmedien",
+ "about": "Über IWM Browser",
+ "quit": "IWM Browser beenden"
+}
diff --git a/browser/i18n/en.js b/browser/i18n/en.js
new file mode 100644
index 0000000..8e9163d
--- /dev/null
+++ b/browser/i18n/en.js
@@ -0,0 +1,61 @@
+{
+ "edit": "Edit",
+ "undo": "Undo",
+ "redo": "Redo",
+ "cut": "Cut",
+ "copy": "Copy",
+ "paste": "Paste",
+ "pasteandmatchstyle": "Paste and Match Style",
+ "delete": "Delete",
+ "selectall": "Select all",
+ "view": "View",
+ "reload": "Reload",
+ "forcereload": "Force Reload",
+ "resetzoom": "Actual size",
+ "zoomin": "Zoom in",
+ "zoomout": "Zoom out",
+ "togglefullscreen": "Toggle Full Screen",
+ "minimalpad": "Minimal Pad",
+ "multiuserbrowser": "Multi-User Browser",
+ "history": "History",
+ "back": "Back",
+ "forward": "Forward",
+ "home": "Home",
+ "recentlyvisited": "Recently Visited",
+ "bookmarks": "Bookmarks",
+ "localfilesystem": "Local Filesystem",
+ "testframes": "Test Frames",
+ "develop": "Develop",
+ "toggledevelopertools": "Toggle Developer Tools",
+ "openprocessmonitor": "Open Process Monitor",
+ "selectfolder": "Select data folder...",
+ "selectfolder.noadmin.ok": "OK",
+ "selectfolder.noadmin.message": "Insufficient permissions",
+ "selectfolder.noadmin.detail": "To change the data directory, the IWM Browser must be started with administrator privileges.",
+ "selectfolder.warning.next": "Next",
+ "selectfolder.warning.cancel": "Cancel",
+ "selectfolder.warning.message": "Data folder exists",
+ "selectfolder.warning.detail": "Your IWM Browser already has a (linked) data directory. If you continue, the old directory is backed up and a new one is created.",
+ "selectfolder.select.title": "Select data folder",
+ "selectfolder.select.buttonLabel": "Select",
+ "selectfolder.samefolder.ok": "OK",
+ "selectfolder.samefolder.message": "Invalid data folder",
+ "selectfolder.samefolder.detail.same": "The old data directory cannot be selected as the new directory.",
+ "selectfolder.samefolder.detail.within": "The new data directory cannot be inside the old directory.",
+ "selectfolder.info.ok": "OK",
+ "selectfolder.info.message": "Created link to data folder",
+ "selectfolder.info.detail": "The IWM Browser now uses the folder \"${0}\" as the new data folder.",
+ "startserver": "Start Server",
+ "stopserver": "Stop Server",
+ "runloadtests": "Run Load Tests",
+ "window": "Window",
+ "close": "Close",
+ "minimize": "Minimize",
+ "zoom": "Zoom",
+ "front": "Bring All to Front",
+ "screenshot": "Make Screenshot",
+ "help": "Help",
+ "iwm": "Leibniz-Institut für Wissensmedien",
+ "about": "About IWM Browser",
+ "quit": "Quit IWM Browser"
+}
diff --git a/browser/main.js b/browser/main.js
new file mode 100644
index 0000000..8b9bbfb
--- /dev/null
+++ b/browser/main.js
@@ -0,0 +1,578 @@
+/* globals require, __dirname, process */
+/*eslint no-console: ["error", { allow: ["log"] }]*/
+
+const { app, BrowserWindow, BrowserView, ipcMain, dialog, shell } = require('electron')
+const electronLocalshortcut = require('electron-localshortcut')
+//const electron = require('electron')
+const os = require('os')
+const fs = require('fs')
+const path = require('path')
+const { URL } = require('url')
+const Store = require('./store.js')
+const { prettyPrint } = require('html')
+
+// Use this constant to start the application in kiosk-mode or in development mode
+const DEVELOPMENT = true
+// true: Dev-Tools are open
+// false (KIOSK-Mode): No application switcher, no menu, no taskbar (or dock on a mac), shortcuts are working
+
+global.multiUserMode = true
+global.errorCount = 0
+global.stopTestsOnError = false
+global.jsonData = { value: null }
+// UO: Experimental feature using Native Windows
+global.useBrowserView = false
+global.useMinimalPad = true
+global.menu = null
+global.observeTraffic = false
+
+// Keep a global reference of the window object, if you don't, the window will
+// be closed automatically when the JavaScript object is garbage collected.
+let win
+let browsers = new Map() // url: BrowserWindow
+
+const store = new Store({
+ // We'll call our data file 'user-preferences'
+ configName: 'user-preferences',
+ defaults: {
+ url: `file://${__dirname}/index.html`,
+ devTools: DEVELOPMENT,
+ multiUserBrowser: true
+ }
+})
+
+function createWindow() {
+
+ if (global.observeTraffic) {
+ const {session} = require('electron')
+ session.defaultSession.webRequest.onCompleted((details) => {
+ console.log("onCompleted", details.url)
+ })
+ }
+ let { screen } = require('electron')
+
+ let bounds = store.get('storedBounds')
+ ? store.get('storedBounds')
+ : screen.getPrimaryDisplay().bounds
+
+ // let displays = screen.getAllDisplays()
+ // let externalDisplay = null
+
+ // externalDisplay = displays[displays.length-1]
+ // const {width, height} =displays[displays.length-1].workAreaSize
+
+ // externalDisplay = displays[0]
+ // const {width, height} =displays[0].workAreaSize
+
+ win = new BrowserWindow({
+ x: bounds.x,
+ y: bounds.y,
+ width: bounds.width,
+ height: bounds.height,
+ fullscreenable: true,
+ fullscreen: !DEVELOPMENT,
+ title: 'IWM Browser',
+ show: false,
+ kiosk: !DEVELOPMENT,
+ acceptFirstMouse: true,
+ webPreferences: {
+ webSecurity: false,
+ allowRunningInsecureContent: true,
+ nodeIntegration: true,
+ webviewTag: true,
+ nativeWindowOpen: true,
+ devTools: true,
+ preload: path.join(__dirname, './preload.js')
+ },
+ icon: path.join(__dirname, 'assets/icons/png/64x64.png')
+ })
+
+ module.exports.win = win
+
+ let url = store.get('url')
+ if (process.argv.length > 2) {
+ let path = process.argv[2]
+ url = `file://${__dirname}/../${path}`
+ console.log('Using process.argv[2]', url)
+ }
+ console.log('Using', url)
+ win.maximize()
+
+ // BAD: All other methods don't work (like ensureFileSync, fileExists...)
+ try {
+ let settings = require('./settings.json')
+ console.log('Using settings', `file://${__dirname}/${settings.url}`)
+ win.loadURL(`file://${__dirname}/${settings.url}`)
+ } catch (ex) {
+ win.loadURL(url)
+ }
+
+ const { webContents } = win
+ //
+ // if (process.platform === 'win32' && win.isKiosk()) {
+ // webContents.on('did-finish-load', function() {
+ // webContents.executeJavaScript('document.body.style.cursor = "none";')
+ // })
+ // }
+
+ // Add the app menu
+ let menu = require('./menu.js')
+ global.menu = menu
+
+ // Add global shortcuts
+ // Esc quits the app
+ electronLocalshortcut.register('Esc', () => {
+ app.quit()
+ })
+
+ // Command (Mac) or Control (Win) + K toggles the Kiosk mode
+ electronLocalshortcut.register('CommandOrControl+K', () => {
+ if (win) {
+ win.setKiosk(!win.isKiosk())
+ }
+ })
+
+ // Show if its ready.
+ win.once('ready-to-show', () => {
+ webContents.send('preparePads')
+ win.show()
+ })
+
+ // Clear cache
+ webContents.session.clearCache(() => console.log('Cache cleared'))
+
+ // Open dev tools when in development mode
+ if (store.get('devTools')) {
+ webContents.openDevTools({ mode: 'right' })
+ } else {
+ webContents.closeDevTools()
+ }
+
+ webContents.on('devtools-opened', () => {
+ store.set('devTools', true)
+ })
+
+ webContents.on('devtools-closed', () => {
+ store.set('devTools', false)
+ })
+
+ webContents.on('did-navigate', (event, url) => {
+ menu.setHistoryStatus()
+ })
+
+ /* UO: At this point we have no access to the event or link position*/
+
+ webContents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures) => {
+ console.log('new-window', global.multiUserMode)
+ if (global.multiUserMode) {
+ event.preventDefault()
+ webContents.send('newPad', url, options.x, options.y)
+ }
+ })
+
+ // WORKAROUND: On windows, if the app was set to fullscreen, the menubar is not hidden
+ if (win.isKiosk()) {
+ win.setMenuBarVisibility(false)
+ }
+
+ win.on('focus', event => {
+ menu.focus()
+ })
+
+ win.on('blur', event => {
+ menu.blur()
+ })
+
+ win.on('enter-full-screen', () => {
+ win.setMenuBarVisibility(false)
+ })
+
+ win.on('leave-full-screen', () => {
+ win.setMenuBarVisibility(true)
+ })
+
+ win.on('enter-html-full-screen', () => {
+ win.setMenuBarVisibility(false)
+ })
+
+ win.on('leave-html-full-screen', () => {
+ win.setMenuBarVisibility(true)
+ })
+ win.on('close', () => {
+ store.set('storedBounds', win.getBounds())
+ })
+
+ // Emitted when the window is closed.
+ win.on('closed', () => {
+ app.quit()
+ })
+}
+
+// When work makes progress, show the progress bar
+function onProgress(progress) {
+ // Use values 0 to 1, or -1 to hide the progress bar
+ try {
+ win.setProgressBar(progress || -1) // Progress bar works on all platforms
+ } catch (e) {
+ if (DEVELOPMENT) console.log(e.message)
+ }
+}
+
+function trySend(target, ...args) {
+ try {
+ target.send(...args)
+ } catch (e) {
+ if (DEVELOPMENT) console.log(e.message)
+ }
+}
+
+function openBrowserView(url, x, y) {
+ const useMinBrowser = false // Change this to switch between Min and a custom browser
+
+ const minURL = 'file:///Users/uo/devel/min/index.html'
+ const browserURL = `file://${__dirname}/browser.html`
+ let width = 640
+ let height = 1200
+ let [winWidth, winHeight] = win.getSize()
+ if (x + width > winWidth) {
+ x = winWidth - width
+ }
+ if (y + height > winHeight) {
+ y = winHeight - height
+ }
+ console.log('open browser view')
+ let browser = new BrowserWindow({
+ x,
+ y,
+ width,
+ height,
+ minWidth: 320,
+ minHeight: 350,
+ titleBarStyle: useMinBrowser ? 'hidden-inset' : 'hidden',
+ frame: process.platform !== 'win32'
+ })
+ let browserContents = browser.webContents
+ browser.setAlwaysOnTop(true)
+ if (useMinBrowser) {
+ browserContents.on('did-finish-load', event => {
+ console.log('did-finish-load', browserContents.getURL())
+
+ browserContents.executeJavaScript(
+ 'Object.values(window.webviews.elementMap).map(obj => obj.src)',
+ result => {
+ console.log(
+ 'window.webviews',
+ result,
+ url,
+ result.indexOf(url)
+ )
+ if (result.indexOf(url) == -1) {
+ console.log('Adding tab')
+ browserContents.send('addTab', { url })
+ }
+ }
+ )
+ })
+ browser.loadURL(minURL)
+ } else {
+ console.log('Loading', browserURL)
+ browser.loadURL(browserURL)
+ let view = new BrowserView({
+ webPreferences: {
+ nodeIntegration: false,
+ devTools: true
+ }
+ })
+ //browserContents.openDevTools({mode: 'right'})
+
+ browser.setBrowserView(view)
+ view.setBounds({ x: 0, y: 24, width: width, height: height - 24 })
+ view.setAutoResize({ width: true, height: true })
+
+ let viewContents = view.webContents
+ let progress = 0
+
+ viewContents.on('page-title-set', event => {
+ console.log('page-title-set', event)
+ })
+ viewContents.on('page-favicon-updated', (event, favicons) => {
+ //console.log("page-favicon-updated", event, favicons)
+ trySend(browserContents, 'favicons', favicons)
+ })
+
+ viewContents.on('did-start-loading', event => {
+ onProgress(0)
+ trySend(browserContents, 'progress', 0)
+ trySend(browserContents, 'did-start-loading')
+ //let senderURL = event.sender.getURL() || url
+ //console.log('did-start-loading', senderURL)
+ })
+ viewContents.on(
+ 'did-get-response-details',
+ (
+ event,
+ status,
+ newURL,
+ originalURL,
+ httpResponseCode,
+ requestMethod,
+ referrer,
+ headers,
+ resourceType
+ ) => {
+ trySend(browserContents, 'did-get-response-details', {
+ status,
+ newURL,
+ originalURL,
+ httpResponseCode,
+ requestMethod,
+ referrer,
+ headers,
+ resourceType
+ })
+ progress += 0.01
+ onProgress(progress)
+ trySend(browserContents, 'progress', progress)
+ //console.log('did-get-response-details', newURL)
+ }
+ )
+ viewContents.on(
+ 'did-get-redirect-request',
+ (
+ event,
+ oldURL,
+ newURL,
+ isMainFrame,
+ httpResponseCode,
+ requestMethod,
+ referrer,
+ headers
+ ) => {
+ trySend(browserContents, 'did-get-redirect-request', {
+ oldURL,
+ newURL,
+ isMainFrame,
+ httpResponseCode,
+ requestMethod,
+ referrer,
+ headers
+ })
+ //console.log('did-get-redirect-request', newURL)
+ }
+ )
+ viewContents.on('did-stop-loading', event => {
+ //console.log('did-stop-loading', event.sender.getURL())
+ trySend(browserContents, 'did-stop-loading')
+ })
+ viewContents.on('did-finish-load', event => {
+ //console.log('did-finish-load', event.sender.getURL())
+ progress = 1
+ onProgress(progress)
+ trySend(browserContents, 'progress', progress)
+ })
+ viewContents.on('dom-ready', event => {
+ if (progress < 0.5) {
+ progress = 0.5
+ onProgress(progress)
+ trySend(browserContents, 'progress', progress)
+ }
+ viewContents.executeJavaScript('document.title', result => {
+ trySend(browserContents, 'title', result)
+ })
+
+ //console.log('dom-ready', event.sender.getURL())
+ })
+
+ viewContents.on('new-window', function (event, url) {
+ event.preventDefault()
+ console.log('new-window')
+ openBrowserView(url, x, y)
+ })
+
+ viewContents.loadURL(url)
+ }
+ browsers.set(url, browser)
+ browser.on('closed', e => {
+ for (let [url, browser] of browsers.entries()) {
+ if (browser == e.sender) {
+ browsers.delete(url)
+ console.log('removed browser view', url)
+ }
+ }
+ })
+}
+
+// UO: Experimental call. Opens a Min Browser window or a limited window with a browser view
+ipcMain.on('loadBrowserView', (e, opts = {}) => {
+ let { url, x, y } = opts
+ openBrowserView(url, x, y)
+})
+
+ipcMain.on('multiUserMode', (e, opts = {}) => {
+ global.multiUserMode = opts
+})
+
+ipcMain.on('padContainerLoaded', e => {
+ win.webContents.send('padContainerAvailable')
+})
+
+ipcMain.on('createScreenshot', (e, opts = {}) => {
+ opts = Object.assign(
+ {},
+ {
+ name: `iwmbrowser-${new Date()
+ .toISOString()
+ .replace(/:/g, '-')}.png`,
+ path: os.tmpdir()
+ },
+ opts
+ )
+
+ win.webContents.capturePage(image => {
+ if (image) {
+ let file = path.join(opts.path, opts.name)
+ fs.writeFile(file, image.toPNG(), err => {
+ if (err) {
+ //throw err
+ } else {
+ console.log(`Screenshot saved: ${file}`)
+ }
+ })
+ }
+ })
+})
+
+ipcMain.on('directoryListing', (e, opts = {}) => {
+ let { directory, files, folders } = opts
+ console.log("directoryListing", opts)
+ try {
+ let listing = fs.readdirSync(directory)
+ let result = { directory, files: [], folders: [] }
+ for (let name of listing) {
+ if (name.startsWith('.'))
+ continue
+ let fullPath = path.join(directory, name)
+ let stat = fs.lstatSync(fullPath)
+ if (files && stat.isFile()) {
+ if (typeof files == 'string' && !files.endsWith(files))
+ continue
+ result.files.push(name)
+ }
+ if (folders && stat.isDirectory())
+ result.folders.push(name)
+ }
+ e.sender.send('directoryListing', result)
+ } catch (err) {
+ let args = { directory, errorMessage: err.message}
+ e.sender.send('directoryListingError', args)
+ }
+})
+
+ipcMain.on('createTextfile', (e, opts = {}) => {
+ opts = Object.assign(
+ {},
+ {
+ name: `iwmbrowser-${new Date()
+ .toISOString()
+ .replace(/:/g, '-')}.txt`,
+ path: os.tmpdir(),
+ text: ''
+ },
+ opts
+ )
+ let file = path.join(opts.path, opts.name)
+ fs.writeFile(file, opts.text, err => {
+ if (err) {
+ //throw err
+ } else {
+ console.log(`Textfile saved: ${file}`)
+ }
+ })
+})
+
+ipcMain.on('error', e => {
+ console.log('Received error notification')
+ global.errorCount += 1
+})
+
+ipcMain.on('openExternal', (e, url=null) => {
+ console.log('Received openExternal', url)
+ if (url) {
+ shell.openExternal(url)
+ }
+})
+
+ipcMain.on('save', (e, opts = {}) => {
+ let { url, html, saveAs, action } = opts
+ // url must absolute URL
+ let urlObj = new URL(url)
+ let pathname = urlObj.pathname
+ if (saveAs) {
+ pathname = dialog.showSaveDialog(win, { title: 'Save as:', defaultPath: pathname })
+ if (typeof pathname == 'undefined')
+ return
+ }
+ try {
+ console.log("Saving", pathname, action)
+ html = prettyPrint(html, { indent_size: 4 });
+ fs.writeFileSync(pathname, html, 'utf-8')
+ if (saveAs) {
+ let normalized = pathname.replace(/\\/g, '/')
+ e.sender.send('savedAs', {url: `file://${normalized}`, action})
+ }
+ }
+ catch (e) {
+ console.warn('Failed to save the file', pathname)
+ e.sender.send('saveFailed', pathname)
+ }
+
+})
+
+// This method will be called when Electron has finished
+// initialization and is ready to create browser windows.
+// Some APIs can only be used after this event occurs.
+app.on('ready', createWindow)
+
+// Quit when all windows are closed.
+app.on('window-all-closed', () => {
+ // On macOS it is common for applications and their menu bar
+ // to stay active until the user quits explicitly with Cmd + Q
+ if (process.platform !== 'darwin') {
+ app.quit()
+ }
+})
+
+app.on(
+ 'select-client-certificate',
+ (event, webContents, url, list, callback) => {
+ console.log('select-client-certificate', url, list)
+ event.preventDefault()
+ ipc.once('client-certificate-selected', (event, item) => {
+ console.log('selected:', item)
+ callback(item)
+ })
+ mainWindow.webContents.send('select-client-certificate', list)
+ }
+)
+
+app.on(
+ 'certificate-error',
+ (event, webContents, url, error, certificate, callback) => {
+ console.log('certificate-error', url)
+ event.preventDefault()
+ const result = true // TODO: do real validation here
+ callback(result)
+ }
+)
+
+app.on('activate', () => {
+ // On macOS it's common to re-create a window in the app when the
+ // dock icon is clicked and there are no other windows open.
+ if (win === null) {
+ createWindow()
+ }
+})
+
+module.exports = {
+ store: store
+}
diff --git a/browser/menu.js b/browser/menu.js
new file mode 100644
index 0000000..c20845b
--- /dev/null
+++ b/browser/menu.js
@@ -0,0 +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
+}
diff --git a/browser/package.js b/browser/package.js
new file mode 100644
index 0000000..a6be579
--- /dev/null
+++ b/browser/package.js
@@ -0,0 +1,171 @@
+/* globals require, __dirname, process */
+/*eslint no-console: ["error", { allow: ["log", "info", "warn", "error"] }]*/
+
+const fse = require('fs-extra')
+const path = require('path')
+const packager = require('electron-packager')
+const rebuild = require('electron-rebuild')
+
+// Arguments
+//----------------------
+let folder = null
+
+if (process.argv.length < 3) {
+ console.error('Missing command line parameter "folder"!')
+ process.exit(1)
+} else {
+ folder = process.argv[2]
+}
+
+// Settings
+//----------------------
+let settings = null
+const root = path.join(__dirname, '../')
+
+try {
+ settings = require(`../${folder}/settings.json`)
+} catch (e) {
+ console.error('Cannot read settings.json in folder, does it exist?')
+ process.exit(1)
+}
+
+// Read settings
+//----------------------
+const title = `--- Build "${settings.name || settings.id}" ---`
+const line = Array(title.length + 1).join('-')
+console.info(line)
+console.info(title)
+console.info(line)
+
+// Using folder
+//----------------------
+const tempFolder = path.join(root, 'temp', settings.id)
+console.log(`Using folder ${tempFolder}`)
+
+// Delete temp folder (when last run aborted)
+fse.removeSync(tempFolder)
+console.log(`Folder ${tempFolder} deleted`)
+
+// Create folder
+fse.ensureDirSync(tempFolder)
+console.log(`Folder ${tempFolder} created`)
+
+// Create subfolders
+const defaultFolders = ['assets', 'browser', 'css', 'lib', 'node_modules', 'server']
+console.log(`The folders ${defaultFolders.join(', ')} are included by default`)
+const folders = new Set(settings.browser.folders.concat(defaultFolders))
+for (let folder of folders) {
+ console.log(`Copy folder ${folder}`)
+ const folderOld = path.join(root, folder)
+ const folderNew = path.join(root, 'temp', settings.id, folder)
+
+ fse.copySync(folderOld, folderNew)
+}
+
+// Write package.json
+//----------------------
+let json = {
+ name: settings.id,
+ productName: settings.name || settings.id,
+ version: settings.version,
+ main: 'browser/main.js',
+ dependencies: {}
+}
+
+// Read and write dependencies
+const packageJson = fse.readJsonSync(path.join(root, 'package.json'))
+Object.assign(json.dependencies, packageJson.dependencies)
+
+// Add browser dependencies
+if (settings.browser.dependencies) {
+ let dependencies = {}
+ for (let dependency of settings.browser.dependencies) {
+ dependencies[dependency] = '*'
+ }
+ Object.assign(json.dependencies, dependencies)
+}
+
+console.log('Create package.json')
+fse.writeJsonSync(path.join(tempFolder, 'package.json'), json, {spaces: 4})
+
+// Write URL to settings.json
+//----------------------
+console.log('Write URL to browser/settings.json')
+fse.writeJsonSync(path.join(tempFolder, 'browser/settings.json'), {url: `../${folder}/index.html`}, {spaces: 4})
+
+// Build with electron-packager
+//----------------------
+console.log('Start electron-packager')
+packager({
+ dir: `./temp/${settings.id}`,
+ arch: 'x64',
+ asar: false,
+ overwrite: true,
+ out: './dist/electron',
+ icon: './assets/icons/icon',
+ platform: settings.browser.platform || ['darwin', 'win32'],
+ prune: false,
+ afterCopy: [(buildPath, electronVersion, platform, arch, callback) => {
+ console.log(`Rebuild Node.js modules for ${platform}...`)
+ rebuild.rebuild({buildPath, electronVersion, arch})
+ .then(() => {
+ console.log(`...Node.js modules for ${platform} rebuilded`)
+ callback()
+ })
+ .catch(error => {
+ console.error(`Error: ${error}`)
+ callback(error)
+ });
+ }]
+})
+ .then(appPaths => {
+ console.log('electron-packager finished')
+
+ // Delete temp folder
+ //----------------------
+ fse.removeSync(tempFolder)
+ console.log(`Folder ${tempFolder} deleted`)
+
+ // Write data folders
+ //----------------------
+ if (settings.browser.data) {
+ console.log('Copy data folders')
+ for (let folder of settings.browser.data) {
+ for (let appPath of appPaths) {
+ console.log(`Copy folder ${folder} to ${appPath}`)
+ const source = path.join(root, folder)
+ const target = path.join(getResourcesPath(root, appPath), folder)
+ fse.copySync(source, target, {
+ dereference: true,
+ filter: item => {
+ if (settings.browser.dataExtensions && fse.lstatSync(item).isFile() && !settings.browser.dataExtensions.includes(path.extname(item).substring(1).toLowerCase())) {
+ return false
+ } else {
+ return true
+ }
+ }
+ })
+ }
+ }
+ }
+
+ // Finished
+ //----------------------
+ console.info('Finished')
+ })
+ .catch(error => {
+ console.error(error)
+ })
+
+function getResourcesPath(root, appPath) {
+
+ let resourcesPath = ""
+
+ if (/darwin/.test(appPath) || /mas/.test(appPath)) {
+ resourcesPath = path.join(root, appPath, `${json.productName}.app/Contents/Resources/app`)
+ } else if (/win32/.test(appPath) || /linux/.test(appPath)) {
+ resourcesPath = path.join(root, appPath, 'resources/app')
+ }
+
+ return resourcesPath
+}
diff --git a/browser/pad.js b/browser/pad.js
new file mode 100644
index 0000000..22cbd5a
--- /dev/null
+++ b/browser/pad.js
@@ -0,0 +1,570 @@
+const {fileURL} = require('./utils.js')
+const path = require('path')
+
+/* A specialization that ignores webview events and thus allows
+ * webviews to get touch, mouse and wheel events.
+ */
+class DOMPadContainer extends DOMScatterContainer {
+
+ capture(event) {
+ if (event.target.tagName === 'WEBVIEW' || event.target.classList.contains('interactiveElement'))
+ return false
+ return super.capture(event)
+ }
+}
+
+/* A wrapper for a webview that behaves like a virtual tablet browser.
+ * Uses a DOMScatter to zoom and rotate the virtual browser window.
+ * The position of buttons and the border size remain constant.
+ */
+class Pad {
+
+ constructor(scatterContainer, {
+ startScale=1.0, minScale=0.25, maxScale=10.5,
+ autoBringToFront=true,
+ url="https://www.iwm-tuebingen.de/www/index.html",
+ hideOnStart=false,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ this.x = x
+ this.y = y
+ this.url = url
+ this.hideOnStart = hideOnStart
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.startScale = startScale
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.frame = document.createElement('div')
+ this.frame.classList.add("pad")
+ this.border = 50 / startScale
+
+ Elements.setStyle(this.frame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ display: 'flex',
+ width: this.width+"px",
+ height: this.height+"px",
+ top: 0,
+ left: 0,
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ overflow: "visible"})
+
+ document.body.appendChild( this.frame)
+
+ this.web=document.createElement("webview")
+ this.webBackground=document.createElement("div")
+ this.webViewSnapshot=document.createElement("img")
+ this.overlay = document.createElement('div')
+
+ this.loadAnim = document.createElement("div")
+ this.loadAnim.style.webkitAnimation= "spin 2s linear infinite"
+ this.loadAnim.style.animation= "spin 2s linear infinite"
+ this.loadAnim.style.position = "absolute"
+
+ document.styleSheets[0].insertRule('\
+ @keyframes spin {\
+ from { transform: rotateZ(0deg); }\
+ to { transform: rotateZ(360deg); }\
+ }'
+ )
+
+ this.overlay.appendChild(this.loadAnim)
+
+ Elements.setStyle(this.web, {
+ position: "absolute",
+ overflow: "auto",
+ border: "1px solid #fff"
+ })
+ // this.web.classList.add("interactiveElement")
+ // this.web.style.pointerEvents="none"
+
+ Elements.setStyle(this.webBackground, {
+ position: "absolute",
+ overflow: "auto",
+ background: "white"
+ })
+
+ Elements.setStyle(this.overlay, {
+ position: "absolute",
+ overflow: "auto",
+ background: "white",
+ opacity: "0.8"
+ })
+
+ Elements.setStyle(this.webViewSnapshot, {
+ position: "absolute",
+ overflow: "auto"
+ })
+
+ let timeTouchStart = 0
+ this.overlayCaptureEvents = document.createElement('div')
+ // overlay.style.background="white"
+
+ this.overlayCaptureEvents.classList.add("interactiveElement")
+
+ this.overlayCaptureEvents.addEventListener('touchmove',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+ })
+
+ this.overlayCaptureEvents.addEventListener('pointerup',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+ let p = {x:e.clientX, y:e.clientY}
+
+ let webviewPosition = Points.fromPageToNode(this.web,p)
+
+ let d = new Date()
+
+ if(d.getTime()-timeTouchStart<150)this.web.sendInputEvent({type:'mouseUp', x: webviewPosition.x, y: webviewPosition.y, button:'left', clickCount: 1})
+ })
+
+ this.overlayCaptureEvents.addEventListener('pointerdown',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+ this.scatter.bringToFront()
+ let p = {x:e.clientX, y:e.clientY}
+
+ let webviewPosition = Points.fromPageToNode(this.web,p)
+
+ let d = new Date()
+ timeTouchStart = d.getTime()
+
+ this.web.sendInputEvent({type:'mouseDown', x: webviewPosition.x, y: webviewPosition.y, button:'left', clickCount: 1})
+ })
+
+ this.overlayCaptureEvents.addEventListener('pointermove',(e)=>{
+ if(e.pointerType!='mouse'){
+ let rotation = Angle.radian2degree(this.scatter.rotation);
+ rotation = (rotation + 360) % 360;
+
+ let r = Math.sqrt(Math.pow(e.movementX, 2) + Math.pow(e.movementY, 2));
+ let phi = Angle.radian2degree(Math.atan2(e.movementX, e.movementY));
+
+ phi = ((phi) + 630) % 360;
+ let rot = ((rotation + 90) + 630) % 360;
+
+ let diffAngle = ((0 + rot) + 360) % 360;
+ let phiCorrected = (phi + diffAngle + 360) % 360;
+
+ let deltaX = r * Math.cos(Angle.degree2radian(phiCorrected));
+ let deltaY = -r * Math.sin(Angle.degree2radian(phiCorrected));
+
+ this.web.executeJavaScript("window.scrollTo(scrollX+"+(-1*deltaX)+", scrollY+"+ (-1*deltaY)+")")
+
+ }
+ })
+
+ this.overlayCaptureEvents.addEventListener('mousewheel',(e)=>{
+ console.log("mousewheel",e.deltaY)
+ // webview.sendInputEvent({type:'mouseWheel', x: 0, y: 0, deltaX: e.deltaX, deltaY: -e.deltaY, canScroll: true })
+ this.web.executeJavaScript("window.scrollTo(scrollX+"+e.deltaX+", scrollY+"+ e.deltaY+")")
+ })
+
+ this.frame.appendChild(this.webBackground)
+ this.frame.appendChild(this.web)
+ this.frame.appendChild(this.webViewSnapshot)
+ this.frame.appendChild(this.overlay)
+ if(remote.getGlobal('multiUserMode'))this.frame.appendChild(this.overlayCaptureEvents)
+
+ this.webViewSnapshot.style.visibility="hidden"
+
+ this.web.src=url
+ this.web.preload= path.join(__dirname, './preloadPad.js')
+
+ this.closeButton = this.addButton("../assets/icons/svg/cross.svg", "close")
+ this.backButton = this.addButton("../assets/icons/svg/left.svg", "go back")
+ this.forwardButton = this.addButton("../assets/icons/svg/right.svg", "go forward")
+
+ this.backButton.style.opacity = 0.5
+ this.forwardButton.style.opacity = 0.5
+
+ /*for (let callback of window.padLoadedHandler) {
+ callback(this, url)
+ }*/
+
+ this.web.addEventListener('new-window', (e) => {
+
+ if(e.url.indexOf("youtube")>-1)return
+
+ if (urlPadMap.has(e.url)) {
+ let childPad = urlPadMap.get(e.url)
+ childPad.scatter.moveTo(x, y)
+ return childPad
+ }
+ let childPad = new Pad(this.scatterContainer, {
+ x: this.scatter.position.x+100,
+ y: this.scatter.position.y+100,
+ url: e.url,
+ width: this.scatter.width,
+ height: this.scatter.height,
+ scalable: true,
+ rotatable: true})
+ urlPadMap.set(e.url, childPad)
+
+ for(let callback of window.padLoadedHandler) {
+ callback(childPad, url)
+ }
+ })
+
+ this.web.addEventListener('did-navigate', (e) => {
+ this.enableButtons()
+ })
+
+ this.web.addEventListener('dom-ready',()=>{
+ //if(this.url.indexOf('local')>-1)this.web.openDevTools()
+ })
+
+ this.web.addEventListener('ipc-message', (e) => {
+ if(e.channel='webviewPointerDown')this.scatter.bringToFront()
+ })
+
+ this.web.addEventListener('did-start-loading', ()=>{
+ this.overlay.style.visibility="visible"
+
+ let w = this.overlay.offsetWidth
+ let h = this.overlay.offsetHeight
+
+ console.log("did start loading",h,w)
+
+ let animationSize = w{
+ this.overlay.style.visibility="hidden"
+ })
+
+ /*this.backButton.addEventListener('click', ()=>{
+ if(this.web.canGoBack())
+ this.web.goBack()
+ })
+
+ this.forwardButton.addEventListener('click', ()=>{
+ if(this.web.canGoForward())
+ this.web.goForward()
+ })
+
+ this.closeButton.addEventListener('click', ()=>{
+ this.close()
+ })*/
+
+ InteractionMapper.on('tap',this.backButton, e => {
+ if(this.web.canGoBack())
+ this.web.goBack()
+ })
+
+ InteractionMapper.on('tap',this.forwardButton, e => {
+ if(this.web.canGoForward())
+ this.web.goForward()
+ })
+
+ InteractionMapper.on('tap',this.closeButton, e => {
+ this.close()
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "../../assets/icons/png/flat/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+
+ this.scatter.moveTo({x, y})
+ this.scatter.bringToFront()
+
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+ this.layout()
+
+ }
+
+ rad2degree(alpha){
+ return alpha * 180 / Math.PI;
+ }
+
+ degree2rad(alpha){
+ return alpha * Math.PI / 180;
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ urlPadMap.delete(this.url)
+ }
+
+ enableButtons() {
+ this.backButton.style.opacity = (this.web.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (this.web.canGoForward()) ? 1 : 0.5
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value="close"
+ button.draggable = false
+ button.classList.add("interactiveElement")
+ this.frame.appendChild(button)
+ return button
+ }
+
+ layout() {
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+
+ this.scatter.resizeButton.style.width=b+"px"
+ this.scatter.resizeButton.style.height=b+"px"
+
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+ let size = "calc(100% - " + (2*b+2) +"px)"
+ Elements.setStyle(this.web, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+ Elements.setStyle(this.webViewSnapshot, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+ Elements.setStyle(this.webBackground, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+
+ Elements.setStyle(this.overlay, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+
+ Elements.setStyle(this.overlayCaptureEvents, {
+ width: size,
+ height: size,
+ opacity: 0.0001,
+ background: "white",
+ margin: b+"px"})
+
+ Elements.setStyle(this.closeButton, {
+ right: (b * 1.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.backButton, {
+ left: (b * 0.8) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.forwardButton, {
+ left: (this.border + (b * 0.8)) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ }
+
+}
+
+class PadFromElement {
+
+ constructor(element, scatterContainer, {
+ startScale=1.0, minScale=0.1, maxScale=1.0,
+ autoBringToFront=true,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ this.element = element
+
+ this.x = x
+ this.y = y
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.frame = document.createElement('div')
+ Elements.setStyle(this.frame, {
+ width: this.width+"px",
+ height: this.height+"px",
+ backgroundColor: "#333",
+ position: "fixed",
+ top: 0,
+ left: 0,
+ overflow: "auto"})
+
+ this.closeButton = this.addButton("../assets/icons/svg/cross.svg", "close")
+
+ document.body.appendChild( this.frame)
+ this.border = 50
+
+ this.frame.appendChild(this.element)
+
+ this.title = document.createElement("div")
+ this.title.innerHTML = "Titel"
+ this.title.style.color = "white"
+ this.frame.appendChild(this.title)
+
+ Elements.setStyle(this.title, {
+ position: "absolute"
+ })
+ // this.element.style.overflow = "auto"
+ // this.element.style.position = "absolute"
+
+ this.layout()
+
+ this.closeButton.addEventListener('click', ()=>{
+ this.frame.style.display="none"
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ this.scatter.bringToFront()
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ this.element.addEventListener('pointerdown', (e) => {
+ this.scatter.bringToFront()
+ })
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ urlPadMap.delete(this.url)
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value="close"
+ button.draggable = false
+ this.frame.appendChild(button)
+ return button
+ }
+
+ layout() {
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+
+ this.scatter.resizeButton.style.width=b+"px"
+ this.scatter.resizeButton.style.height=b+"px"
+
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+ let size = "calc(100% - " + (2*b+2) +"px)"
+ Elements.setStyle(this.element, {
+ width: size,
+ height: size,
+ top: b+"px",
+ left: b+"px"})
+ Elements.setStyle(this.closeButton, {
+ right: (b * 0.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.title, {
+ left: (b * 1.5) + "px",
+ fontSize: (b * 0.8) + "px",
+ top: (0.1)+"0px"})
+ }
+}
+
+module.exports = { Pad, DOMPadContainer, PadFromElement }
diff --git a/browser/padAccordion.js b/browser/padAccordion.js
new file mode 100644
index 0000000..35e81df
--- /dev/null
+++ b/browser/padAccordion.js
@@ -0,0 +1,3540 @@
+const {fileURL} = require('./utils.js')
+const path = require('path')
+const electron = require('electron');
+const BrowserWindow = electron.remote.BrowserWindow;
+
+/* A specialization that ignores webview events and thus allows
+ * webviews to get touch, mouse and wheel events.
+ */
+class DOMPadContainer extends DOMScatterContainer {
+
+ capture(event) {
+ if (event.target.tagName === 'WEBVIEW' || event.target.classList.contains('interactiveElement'))
+ return false
+ return super.capture(event)
+ }
+}
+
+/* A wrapper for a webview that behaves like a virtual tablet browser.
+ * Uses a DOMScatter to zoom and rotate the virtual browser window.
+ * The position of buttons and the border size remain constant.
+ */
+class Pad {
+
+ constructor(scatterContainer, {
+ startScale=1.0, minScale=0.25, maxScale=10.5,
+ autoBringToFront=true,
+ title='new Pad',
+ type='pad',
+ url="https://www.iwm-tuebingen.de/www/index.html",
+ hideOnStart=false,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ this.x = x
+ this.y = y
+ this.type = type
+ this.title=title
+ this.url = url
+ this.hideOnStart = hideOnStart
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.startScale = startScale
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.frame = document.createElement('div')
+ this.frame.classList.add("pad")
+ this.border = 50 / startScale
+
+ Elements.setStyle(this.frame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ display: 'flex',
+ width: this.width+"px",
+ height: this.height+"px",
+ top: 0,
+ left: 0,
+ visibility: "visible",
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ overflow: "visible"})
+
+ document.body.appendChild( this.frame)
+
+ this.web=document.createElement("webview")
+ this.webBackground=document.createElement("div")
+ this.webViewSnapshot=document.createElement("img")
+ this.overlay = document.createElement('div')
+
+ this.loadAnim = document.createElement("div")
+ this.loadAnim.style.webkitAnimation= "spin 2s linear infinite"
+ this.loadAnim.style.animation= "spin 2s linear infinite"
+ this.loadAnim.style.position = "absolute"
+
+ document.styleSheets[0].insertRule('\
+ @keyframes spin {\
+ from { transform: rotateZ(0deg); }\
+ to { transform: rotateZ(360deg); }\
+ }'
+ )
+
+ this.overlay.appendChild(this.loadAnim)
+
+ Elements.setStyle(this.web, {
+ position: "absolute",
+ overflow: "auto",
+ border: "1px solid #fff"
+ })
+
+ Elements.setStyle(this.webBackground, {
+ position: "absolute",
+ overflow: "auto",
+ background: "white"
+ })
+
+ Elements.setStyle(this.overlay, {
+ position: "absolute",
+ overflow: "auto",
+ background: "white",
+ opacity: "0.8"
+ })
+
+ Elements.setStyle(this.webViewSnapshot, {
+ position: "absolute",
+ overflow: "auto"
+ })
+
+ this.frame.appendChild(this.webBackground)
+ this.frame.appendChild(this.web)
+ this.frame.appendChild(this.webViewSnapshot)
+ // this.frame.appendChild(this.overlay)
+
+ this.webViewSnapshot.style.visibility="hidden"
+
+ this.web.src=url
+ this.web.preload= path.join(__dirname, './preloadPad.js')
+
+ this.closeButton = this.addButton("../dev/tumortisch/icons/svg/cross.svg", "close")
+ this.backButton = this.addButton("../dev/tumortisch/icons/svg/left.svg", "go back")
+ this.forwardButton = this.addButton("../dev/tumortisch/icons/svg/right.svg", "go forward")
+
+ this.backButton.style.opacity = 0.5
+ this.forwardButton.style.opacity = 0.5
+
+ /*for (let callback of window.padLoadedHandler) {
+ callback(this, url)
+ }*/
+ this.layout()
+
+ this.web.addEventListener('new-window', (e) => {
+
+ if(e.url.indexOf("youtube")>-1)return
+
+ if (urlPadMap.has(e.url)) {
+ let childPad = urlPadMap.get(e.url)
+ childPad.scatter.moveTo(x, y)
+ return childPad
+ }
+ let childPad = new Pad(this.scatterContainer, {
+ x: this.scatter.position.x+100,
+ y: this.scatter.position.y+100,
+ url: e.url,
+ width: this.scatter.width,
+ height: this.scatter.height,
+ scalable: true,
+ rotatable: true})
+ urlPadMap.set(e.url, childPad)
+
+ for(let callback of window.padLoadedHandler) {
+ callback(childPad, url)
+ }
+ })
+
+ this.web.addEventListener('did-navigate', (e) => {
+ this.enableButtons()
+ })
+
+ this.web.addEventListener('dom-ready',()=>{
+ //if(this.url.indexOf('local')>-1)this.web.openDevTools()
+ })
+
+ this.web.addEventListener('ipc-message', (e) => {
+ if(e.channel=='webviewPointerDown')this.scatter.bringToFront()
+ })
+
+ this.web.addEventListener('did-start-loading', ()=>{
+ this.overlay.style.visibility="visible"
+
+ let w = this.overlay.offsetWidth
+ let h = this.overlay.offsetHeight
+
+ // console.log("did start loading",h,w)
+
+ let animationSize = w{
+ this.overlay.style.visibility="hidden"
+ let query="#"+this.title+"expandButton"
+ $(query).css("opacity","1.0")
+ })
+
+ this.backButton.addEventListener('click', (e)=>{
+ if(this.web.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goBack()
+ }
+ })
+
+ this.forwardButton.addEventListener('click', (e)=>{
+ if(this.web.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goForward()
+ }
+ })
+
+ this.closeButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "./icons/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+ // this.scatter.resizeButton.className = "frameButton"
+
+ this.scatter.moveTo({x, y})
+ this.scatter.bringToFront()
+
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ }
+
+ rad2degree(alpha){
+ return alpha * 180 / Math.PI;
+ }
+
+ degree2rad(alpha){
+ return alpha * Math.PI / 180;
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ urlPadMap.delete(this.url)
+ }
+
+ enableButtons() {
+ this.backButton.style.opacity = (this.web.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (this.web.canGoForward()) ? 1 : 0.5
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value=value
+ button.draggable = false
+ button.classList.add("interactiveElement")
+ this.frame.appendChild(button)
+ return button
+ }
+
+ layout() {
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+ let size = "calc(100% - " + (2*b+2) +"px)"
+ Elements.setStyle(this.web, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+ Elements.setStyle(this.webViewSnapshot, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+ Elements.setStyle(this.webBackground, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+
+ Elements.setStyle(this.overlay, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+ Elements.setStyle(this.closeButton, {
+ right: (b * 1.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.backButton, {
+ left: (b * 0.8) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.forwardButton, {
+ left: (this.border + (b * 0.8)) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ }
+
+}
+class PadAccordion {
+
+ constructor(scatterContainer, {
+ startScale=1.0, minScale=0.25, maxScale=10.5,
+ autoBringToFront=true,
+ type = 'accordion',
+ title="new Pad",
+ urlList=["https://www.iwm-tuebingen.de/www/index.html"],
+ hideOnStart=false,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ minimizedHiddenHeight = 0,
+ minimizedHiddenWidth = 0,
+ minimized = false,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ let w = window,
+ d = document,
+ e = d.documentElement,
+ g = d.getElementsByTagName('body')[0]
+ this.pageWidth = w.innerWidth || e.clientWidth || g.clientWidth,
+ this.pageHeight = w.innerHeight|| e.clientHeight|| g.clientHeight
+
+ this.x = x
+ this.y = y
+ this.type = type
+ this.title=title
+ this.urlList = urlList
+ this.hideOnStart = hideOnStart
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.startScale = startScale
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+ this.minimizedHiddenHeight = minimizedHiddenHeight
+ this.minimizedHiddenWidth = minimizedHiddenWidth
+ this.minimized = minimized
+
+ this.webviewID = 0
+ this.externalPadID = 0
+ this.currentWebviewBackground = null
+ this.currentWebviewFrame = null
+ this.currentWebview = null
+ this.currentWebviewImage = null
+ this.currentSource=""
+ this.webviewMap = new Map()
+ this.padMap = new Map()
+ this.frame = document.createElement('div')
+ this.webFrame = document.createElement('div')
+ this.titlebar = document.createElement('div')
+ this.frame.classList.add("pad")
+ this.border = this.pageHeight*0.023148 / startScale
+
+ this.zIndex=1000
+ this.numWebviewsReady = 0
+
+ this.closeAllButton = this.addButton("../dev/tumortisch/icons/clear_all.png", "closeAll")
+ this.minimizeButton = this.addButton("../dev/tumortisch/icons/remove.png", "minimize")
+ this.maximizeButton = this.addButton("../dev/tumortisch/icons/fullscreen.png", "maximize")
+ this.closeButton = this.addButton("../dev/tumortisch/icons/clear.png", "closeSingle")
+ this.backButton = this.addButton("../dev/tumortisch/icons/left.svg", "go back")
+ this.forwardButton = this.addButton("../dev/tumortisch/icons/right.svg", "go forward")
+ this.expandButton = this.addButton("../dev/tumortisch/icons/launch.png", "expand")
+
+ this.expandButton.id=this.title+"expandButton"
+
+ this.minimized == false ? this.minimizeButton.src = fileURL("../dev/tumortisch/icons/remove.png") : this.minimizeButton.src = fileURL("../dev/tumortisch/icons/fullscreen.png")
+
+ this.backButton.style.opacity = 0.5
+ this.forwardButton.style.opacity = 0.5
+ TweenMax.to(this.maximizeButton, 0.0, {scale:"0"})
+
+ /*let pad = new Pad(nodePadContainer, {
+ x: 500,
+ y: 200,
+ title: this.title,
+ url: "var/blank.html",
+ startScale:1,
+ hideOnStart: false,
+ width: 1500,
+ height: 1500,
+ scalable: true,
+ rotatable: false})
+
+
+ this.bufferPad=pad*/
+
+ // this.bufferPad.frame.id = this.title+"bufferPadFrame"
+ // this.bufferPad.web.id = this.title+"bufferPadWeb"
+ // this.bufferPad.frame.style.visibility="hidden"
+
+ Elements.setStyle(this.frame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ width: this.width+"px",
+ height: this.height+"px",
+ visibility: "visible",
+ top: 0,
+ left: 0
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ })
+
+ Elements.setStyle(this.webFrame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ // display: 'flex',
+ // flexFlow: "row no-wrap",
+ overflowX: "hidden",
+ width: "100%",
+ height: "80%",
+ top: 0,
+ left: 0
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ })
+
+ this.titlebar.innerHTML = this.title
+ Elements.setStyle(this.titlebar, {
+ color: "#fff",
+ // backgroundColor: "#F53",
+ // innerHTML:"title",
+ position: "absolute",
+ // display: 'flex',
+ // flexFlow: "row no-wrap",
+ overflowX: "hidden"
+ })
+
+ this.clicker=document.createElement('div')
+
+ Elements.setStyle(this.clicker, {
+ backgroundColor: "#F53",
+ position: "absolute",
+ width: "60px",
+ height: "50px",
+ bottom: 0,
+ left: 0
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ })
+
+
+ document.body.appendChild( this.frame)
+ this.frame.appendChild( this.webFrame)
+ this.frame.appendChild( this.titlebar)
+ // this.frame.appendChild( this.clicker)
+
+ this.clicker.addEventListener('click',(e)=>{
+ this.arrangeWebviews()
+ })
+
+ for(var i=0; i {
+
+ if(e.url.indexOf("youtube")>-1)return
+
+ if (urlPadMap.has(e.url)) {
+ let childPad = urlPadMap.get(e.url)
+ childPad.scatter.moveTo(x, y)
+ return childPad
+ }
+ let childPad = new Pad(this.scatterContainer, {
+ x: this.scatter.position.x+100,
+ y: this.scatter.position.y+100,
+ url: e.url,
+ width: this.scatter.width,
+ height: this.scatter.height,
+ scalable: true,
+ rotatable: true})
+ urlPadMap.set(e.url, childPad)
+
+ for(let callback of window.padLoadedHandler) {
+ callback(childPad, url)
+ }
+ })
+
+ this.web.addEventListener('did-navigate', (e) => {
+ this.enableButtons()
+ })
+
+ this.web.addEventListener('dom-ready',()=>{
+ //if(this.url.indexOf('local')>-1)this.web.openDevTools()
+ })
+
+ this.web.addEventListener('ipc-message', (e) => {
+ if(e.channel=='webviewPointerDown')this.scatter.bringToFront()
+ })
+
+ this.web.addEventListener('did-start-loading', ()=>{
+ this.overlay.style.visibility="visible"
+
+ let w = this.overlay.offsetWidth
+ let h = this.overlay.offsetHeight
+
+ console.log("did start loading",h,w)
+
+ let animationSize = w{
+ this.overlay.style.visibility="hidden"
+ })*/
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "./icons/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+
+ this.scatter.moveTo({x, y})
+ this.scatter.bringToFront()
+
+ this.currentWebviewHeight = 0
+
+ this.scatter.element.addEventListener('resized',(e)=>{
+ // $(this.webFrame).find('.accordionWebimage').css('height',this.currentWebviewHeight)
+
+ this.arrangeWebviews()
+ // console.log("resized",e,e.detail)
+ })
+
+ this.scatter.element.addEventListener('resizeStarted',()=>{
+ // this.currentWebviewHeight = this.webFrame.offsetHeight
+ // console.log("resizeStarted")
+ })
+
+ this.scatter.element.addEventListener('resizeEnded',()=>{
+ /*let h = this.webFrame.offsetHeight
+ let cWebview = this.currentWebview
+ $(this.webFrame).find('.accordionWebview').each(function(i, obj) {
+ // obj.style.visibility="visible"
+ if(obj!=cWebview){
+ obj.style.visibility="visible"
+ obj.capturePage((image)=>{
+ obj.parentElement.childNodes[3].src=image.toDataURL()
+
+ obj.style.visibility="hidden"
+ $(this.webFrame).find('.accordionWebimage').css('height',h)
+ })
+
+ }
+ })*/
+ // console.log("resizeEnded")
+ })
+
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = this.pageHeight*0.023148 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ else{
+ }
+ })
+ this.layout()
+
+ InteractionMapper.on('tap',this.backButton, e => {
+ if(this.currentWebview.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.currentWebview.goBack()
+ }
+ })
+
+ InteractionMapper.on('tap',this.forwardButton, e => {
+ if(this.currentWebview.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.currentWebview.goForward()
+ }
+ })
+
+ InteractionMapper.on('tap',this.closeAllButton, e => {
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })
+
+ InteractionMapper.on('tap',this.minimizeButton, e => {
+ let s = this.scatter.scale
+ let x=0
+ let y=0
+ let deltaX = 0
+ let deltaY = 0
+ switch(this.minimized){
+ case false:
+
+ let w = this.frame.offsetWidth*s < 800 ? this.frame.offsetWidth : 800/s
+
+ x = this.frame.getBoundingClientRect().right - w
+ y = this.frame.getBoundingClientRect().bottom - this.border
+
+ deltaX = this.frame.offsetWidth - w
+ deltaY = this.frame.offsetHeight - this.border
+
+
+ this.minimizedHiddenHeight = this.frame.offsetHeight
+ this.minimizedHiddenWidth = this.frame.offsetWidth
+
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+
+ this.minimize(w, this.border, this.scatter.scale, deltaX*s, deltaY*s)
+ this.minimized = true
+ this.minimizeButton.src = fileURL("../dev/tumortisch/icons/fullscreen.png")
+ this.scatter.scalable = false
+ // this.frame.style.pointerEvents = "none"
+ // this.minimizeButton.style.pointerEvents = "all"
+ // this.closeAllButton.style.pointerEvents = "all"
+ $(this.frame).children(".frameButton").each(function(i, obj) {
+ if(obj.value !="close" && obj.value !="closeAll" && obj.value !="minimize")obj.style.pointerEvents = "none"
+ })
+ break
+ case true:
+
+ s = this.scatter.scale
+
+ x = this.frame.getBoundingClientRect().right - this.minimizedHiddenWidth
+ y = this.frame.getBoundingClientRect().bottom - this.minimizedHiddenHeight
+
+ deltaX = this.frame.getBoundingClientRect().width - this.minimizedHiddenWidth*s
+ deltaY = this.frame.getBoundingClientRect().height - this.minimizedHiddenHeight*s
+
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+
+ this.minimize(this.minimizedHiddenWidth, this.minimizedHiddenHeight, this.scatter.scale, deltaX, deltaY)
+ this.minimized = false
+ this.minimizeButton.src = fileURL("../dev/tumortisch/icons/remove.png")
+ this.arrangeWebviews()
+ this.scatter.scalable = true
+ $(this.frame).children(".frameButton").each(function(i, obj) {
+ obj.style.pointerEvents = "all"
+ })
+ // this.frame.style.pointerEvents = "all"
+ break
+ }
+ })
+
+ /* InteractionMapper.on('tap',this.minimizeButton, e => {
+
+ let p = {x:e.center.x, y:e.center.y}
+
+ // let framePosition = Points.fromPageToNode(this.frame,p)
+
+ let s = this.scatter.scale
+
+ let w = this.frame.offsetWidth*s < 800 ? this.frame.offsetWidth : 800/s
+
+ // let x = this.frame.getBoundingClientRect().x + this.frame.offsetWidth*s - w
+ // let y = this.frame.getBoundingClientRect().y + this.frame.offsetHeight*s - this.border
+
+ let x = this.frame.getBoundingClientRect().right - w
+ let y = this.frame.getBoundingClientRect().bottom - this.border
+
+ let deltaX = this.frame.offsetWidth - w
+ let deltaY = this.frame.offsetHeight - this.border
+
+ let tmp = this.frame.getBoundingClientRect().bottom - this.frame.getBoundingClientRect().top
+
+ console.log("minimizeButton", s, this.frame.getBoundingClientRect().x, this.frame.offsetHeight*s, this.frame.offsetWidth*s, tmp)
+
+ this.currentHeight = this.frame.offsetHeight
+ this.currentWidth = this.frame.offsetWidth
+ this.currentWebHeight = this.webFrame.offsetHeight
+ this.currentScale = s
+
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ // TweenMax.to(this.webFrame, 1.1, {height:"0px"})
+ // TweenMax.to(this.frame, 0.2, {height:this.border, width:w})
+ this.resize(w, this.border, this.scatter.scale, deltaX*s, deltaY*s)
+
+ TweenMax.to(this.minimizeButton, 0.0, {scale:"0"})
+ TweenMax.to(this.maximizeButton, 0.0, {scale:"1"})
+
+ })
+
+ InteractionMapper.on('tap',this.maximizeButton, e => {
+ console.log("maximizeButton", this.frame.getBoundingClientRect().width, this.currentWidth, this.frame.getBoundingClientRect().width - this.currentWidth)
+ let p = {x:e.center.x, y:e.center.y}
+
+ // let framePosition = Points.fromPageToNode(this.frame,p)
+
+ let s = this.scatter.scale
+
+ // let r = this.frame.offsetWidth - framePosition.x
+ // let b = this.frame.offsetHeight - framePosition.y
+
+ // let x = this.frame.getBoundingClientRect().x + this.frame.offsetWidth - this.currentWidth
+ // let y = this.frame.getBoundingClientRect().y + this.frame.offsetHeight - this.currentHeight
+
+ let x = this.frame.getBoundingClientRect().right - this.currentWidth
+ let y = this.frame.getBoundingClientRect().bottom - this.currentHeight
+
+ let deltaX = this.frame.getBoundingClientRect().width - this.currentWidth*s
+ let deltaY = this.frame.getBoundingClientRect().height - this.currentHeight*s
+
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ // TweenMax.to(this.webFrame, 0.1, {height:this.currentWebHeight+"px"})
+ this.resize(this.currentWidth, this.currentHeight, this.scatter.scale, deltaX, deltaY)
+ // TweenMax.to(this.frame, 0.2, {height: this.currentHeight+"px", width: this.currentWidth+"px", x: x, y: y})
+
+ TweenMax.to(this.minimizeButton, 0.0, {scale:"1"})
+ TweenMax.to(this.maximizeButton, 0.0, {scale:"0"})
+ }) */
+
+ InteractionMapper.on('tap',this.closeButton, e => {
+ if(this.webviewMap.size>1)this.removeWebView()
+ })
+
+ InteractionMapper.on('tap',this.expandButton, e => {
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.externalPadID++
+ let newPadKey=this.title+this.externalPadID
+ console.log("NEW PAD KEY",newPadKey)
+ console.log("NEW PAD ACCORDION",this.currentWebview.src)
+
+ let newPad= new minimalPad(nodePadContainer, {
+ x: 500,
+ y: 200,
+ padMapKey: newPadKey,
+ title: this.title+" - "+ this.expandButton.getAttribute('source'),//this.currentWebview.getTitle(),
+ urlList: [this.currentWebview.src],
+ startScale:1,
+ hideOnStart: false,
+ width: this.pageWidth*0.3906,
+ height: this.pageWidth*0.3906,
+ scalable: true,
+ rotatable: false})
+
+ urlPadMap.set(newPadKey,newPad)
+ })
+
+ /*this.backButton.addEventListener('click', (e)=>{
+ if(this.currentWebview.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.currentWebview.goBack()
+ }
+ })
+
+ this.forwardButton.addEventListener('click', (e)=>{
+ if(this.currentWebview.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.currentWebview.goForward()
+ }
+ })
+
+ this.closeAllButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })
+
+ this.closeButton.addEventListener('click', (e)=>{
+ if(this.webviewMap.size>1)this.removeWebView()
+ })
+
+ this.expandButton.addEventListener('click',(e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.externalPadID++
+ let newPadKey=this.title+this.externalPadID
+ console.log("NEW PAD KEY",newPadKey)
+ console.log("NEW PAD ACCORDION",this.currentWebview.src)
+ // console.log("expanding",this.currentWebview.getTitle())
+ // console.log("expanding",this.currentWebview.getURL())
+
+ let newPad= new minimalPad(nodePadContainer, {
+ x: 500,
+ y: 200,
+ padMapKey: newPadKey,
+ title: this.title+" - "+ this.expandButton.getAttribute('source'),//this.currentWebview.getTitle(),
+ urlList: [this.currentWebview.src],
+ startScale:1,
+ hideOnStart: false,
+ width: this.pageWidth*0.3906,
+ height: this.pageWidth*0.3906,
+ scalable: true,
+ rotatable: false})
+
+ /* let newPad= new PadAccordion(nodePadContainer, {
+ x: 500,
+ y: 200,
+ padMapKey: newPadKey,
+ title: this.title+" - "+ this.expandButton.getAttribute('source'),//this.currentWebview.getTitle(),
+ urlList: [{'url':this.currentWebview.src,'source':this.expandButton.getAttribute('source')}],
+ startScale:1,
+ hideOnStart: false,
+ width: this.pageWidth*0.3906,
+ height: this.pageWidth*0.3906,
+ scalable: true,
+ rotatable: false}) */
+
+ // urlPadMap.set(newPadKey,newPad)
+ /* document.getElementById(this.title+"bufferPadFrame").style.visibility="visible"
+ document.getElementById(this.title+"bufferPadFrame").id=""
+ document.getElementById(this.title+"bufferPadWeb").id=""*/
+ /*if(e.target.style.opacity=="1"){
+ this.bufferPad.frame.style.visibility="visible"
+ this.bufferPad.scatter.bringToFront()
+ // this.bufferPad.frame.id=""
+ // this.bufferPad.web.id=""
+
+ let newPad= new Pad(nodePadContainer, {
+ x: 500,
+ y: 200,
+ title: this.title,
+ url: "var/blank.html",
+ startScale:1,
+ hideOnStart: false,
+ width: 1500,
+ height: 1500,
+ scalable: true,
+ rotatable: false})
+
+ this.bufferPad=newPad
+ // newPad.web.src=this.currentWebview.src
+ // this.bufferPad.frame.id = this.title+"bufferPadFrame"
+ // this.bufferPad.web.id = this.title+"bufferPadWeb"
+ this.bufferPad.frame.style.visibility = "hidden"
+ this.bufferPad.web.src=this.currentWebview.src
+ }*/
+ // })
+ // TweenMax.to(this.frame,0,{opacity:"0.001"})
+
+ }
+
+ minimize(width, height, scale, x, y) {
+
+ let oldPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+
+ this.frame.style.transformOrigin = "0% 0%"
+
+ let newPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+
+ let offset = Points.subtract(oldPostition, newPostition)
+
+ TweenLite.to(this.frame, 0, { css: { left: "+=" + offset.x + "px" } })
+ TweenLite.to(this.frame, 0, { css: { top: "+=" + offset.y + "px" } })
+
+ // TweenLite.to(this.frame, 0, { width: width , height: height, x: x, y: y })
+ TweenLite.to(this.frame, 0, { width: width , height: height})
+ TweenLite.to(this.frame, 0, { css: { left: "+=" + x + "px" } })
+ TweenLite.to(this.frame, 0, { css: { top: "+=" + y + "px" } })
+
+ oldPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+ this.frame.style.transformOrigin = "50% 50%"
+ newPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+ offset = Points.subtract(oldPostition, newPostition)
+
+ TweenLite.to(this.frame, 0, { css: { left: "+=" + offset.x + "px" } })
+ TweenLite.to(this.frame, 0, { css: { top: "+=" + offset.y + "px" } })
+
+ }
+
+ removeWebView(){
+ let webVIewRemovedID=parseInt(this.currentWebview.getAttribute('id'))
+
+ this.webviewMap.delete(webVIewRemovedID)
+ this.webFrame.removeChild(this.currentWebview.parentNode)
+
+ let cWeb = null
+ let cWebviewFrame = null
+
+ this.webviewMap.forEach(function (entry, key) {
+ cWeb = entry.childNodes[3]
+ cWebviewFrame = entry
+ })
+ this.currentWebview = cWeb
+ this.currentWebviewFrame = cWebviewFrame
+ this.currentSource = cWeb.src
+ this.bringWebToFront()
+ }
+
+ appendWebView(newUrl,source){
+ let fs = nodeRequire("fs")
+ let timeTouchStart = 0
+
+ let web=document.createElement("webview")
+ let webOffloading=document.createElement("webview")
+ let webImage=document.createElement("img")
+ let dummyImage=document.createElement("img")
+ let heinz=document.createElement("img")
+ let webBackground=document.createElement("div")
+ let webviewFrame=document.createElement("div")
+ let shadow=document.createElement("img")
+ let shadowReverse=document.createElement("img")
+ let overlay = document.createElement('img')
+ let overlayCaptureEvents = document.createElement('div')
+ let progressBar = document.createElement('div')
+
+ web.setAttribute("plugins",true)
+ web.classList.add("accordionWebview")
+ // web.classList.add("interactiveElement")
+ webviewFrame.classList.add("accordionWebviewFrame")
+ shadow.classList.add("accordionWebviewShadow")
+ shadowReverse.classList.add("accordionWebviewShadow")
+ webBackground.classList.add("accordionWebviewBackground")
+ webBackground.setAttribute('source',source)
+ webImage.classList.add("accordionWebimage")
+ overlay.classList.add("accordionOverlay")
+ overlay.src="img/overlay.png"
+
+ overlayCaptureEvents.classList.add("interactiveElement")
+
+ this.expandButton.setAttribute('source',source)
+
+ webviewFrame.classList.add("interactiveElement")
+ webBackground.classList.add("interactiveElement")
+ webImage.classList.add("interactiveElement")
+ overlay.classList.add("interactiveElement")
+ web.classList.add("interactiveElement")
+
+ let loadAnim = document.createElement("div")
+ loadAnim.style.webkitAnimation= "spin 2s linear infinite"
+ loadAnim.style.animation= "spin 2s linear infinite"
+ loadAnim.style.position = "absolute"
+
+ document.styleSheets[0].insertRule('\
+ @keyframes spin {\
+ from { transform: rotateZ(0deg); }\
+ to { transform: rotateZ(360deg); }\
+ }'
+ )
+
+ // overlay.appendChild(loadAnim)
+
+ // webBackground.className= "item"
+
+ Elements.setStyle(webviewFrame, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: this.webviewID*50+"px",
+ // display:"flex",
+ width: "20%",
+ // pointerEvents: 'none',
+ left: "0%",
+ // minWidth:"5px",
+ // maxWidth:"500px",
+ height: "100%"
+ // boxShadow: "-5px 5px 50px 10px #555",
+ // border:"2px solid blue",
+ // overflow: "hidden",
+ // overflowX: "scroll",
+ // flex: "0 0 50%",
+ // whiteSpace: "nowrap",
+ })
+
+ Elements.setStyle(webBackground, {
+ position: "absolute",
+ overflow: "hidden",
+ left: "5%",
+ // display:"flex",
+ width: "90%",
+ pointerEvents: 'none',
+ // minWidth:"5px",
+ // maxWidth:"500px",
+ height: "100%",
+ // boxShadow: "-5px 5px 50px 10px #555",
+ // border:"2px solid blue",
+ // overflow: "hidden",
+ // overflowX: "scroll",
+ // flex: "0 0 50%",
+ // whiteSpace: "nowrap",
+ background: "white"
+ })
+
+ shadow.src = "img/shadow.png"
+ Elements.setStyle(shadow, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "95%",
+ width: "5%",
+ height: "100%",
+ // background: "#252",
+ // opacity: "0.1",
+ pointerEvents:"none"
+ })
+
+ shadowReverse.src = "img/shadowReverse.png"
+ Elements.setStyle(shadowReverse, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "0%",
+ width: "5%",
+ height: "100%",
+ // background: "#252",
+ // opacity: "0.1",
+ pointerEvents:"none"
+ })
+
+ Elements.setStyle(web, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "5%",
+ width: "90%",
+ // pointerEvents: 'none',
+ // filter: "blur(10px)",
+ // display: "none",
+ visibility: "hidden",
+ // border: "1px solid #fff",
+ height: "100%"
+ })
+
+ Elements.setStyle(webOffloading, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "-100%",
+ width: "90%",
+ // pointerEvents: 'none',
+ // filter: "blur(10px)",
+ // display: "none",
+ visibility: "visible",
+ // border: "1px solid #fff",
+ height: "100%"
+ })
+
+ Elements.setStyle(webImage, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "5%",
+ width: "90%",
+ pointerEvents: 'none',
+ // display: "none",
+ visibility: "visible",
+ // border: "1px solid #fff",
+ height: this.webFrame.offsetHeight+"px"
+ })
+
+ Elements.setStyle(dummyImage, {
+ visibility: "visible"
+ })
+
+ Elements.setStyle(heinz, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "0px",
+ width: "100%",
+ background: "pink",
+ // display: "none",
+ visibility: "visible",
+ // border: "1px solid #fff",
+ height: "100%"
+ })
+
+ Elements.setStyle(overlay, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "5%",
+ // background: "pink",
+ // pointerEvents: 'none',
+ width: "90%",
+ height: "100%"
+ // opacity: "0.1"
+ })
+
+ Elements.setStyle(overlayCaptureEvents, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "5%",
+ background: "white",
+ // pointerEvents: 'none',
+ width: "90%",
+ height: "100%",
+ opacity: "0.0001"
+ })
+
+ Elements.setStyle(progressBar, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0%",
+ left: "10%",
+ visibility: "hidden",
+ background: "white",
+ filter: "blur(50px)",
+ opacity: "0.2",
+ // pointerEvents: 'none',
+ width: "40%",
+ height: "100%"
+ // opacity: "0.1"
+ })
+
+
+ this.currentWebviewFrame=webviewFrame
+ this.currentWebviewBackground=webBackground
+ this.currentWebview = web
+ this.currentWebviewImage = webImage
+ this.currentSource = source
+
+ // document.body.appendChild(webOffloading)
+
+ overlayCaptureEvents.addEventListener('touchmove',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+ })
+
+ overlayCaptureEvents.addEventListener('pointerup',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+
+ let d = new Date()
+ console.log("delta Time",d.getTime()-timeTouchStart)
+ if(d.getTime()-timeTouchStart<150)web.sendInputEvent({type:'mouseUp', x: (e.clientX-web.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-web.getBoundingClientRect().top)/this.scatter.scale, button:'left', clickCount: 1})
+ })
+
+ overlayCaptureEvents.addEventListener('pointerdown',(e)=>{
+ web.send('overlayEvent')
+ e.preventDefault()
+ e.stopPropagation()
+
+ let d = new Date()
+ timeTouchStart = d.getTime()
+ console.log("timeTouchStart",timeTouchStart)
+ web.sendInputEvent({type:'mouseDown', x: (e.clientX-web.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-web.getBoundingClientRect().top)/this.scatter.scale, button:'left', clickCount: 1})
+ })
+
+ overlayCaptureEvents.addEventListener('pointermove',(e)=>{
+ if(e.pointerType!='mouse'){
+ // console.log("e.movementY",e.movementY)
+ web.executeJavaScript("window.scrollTo(scrollX+"+(-1*e.movementX)+", scrollY+"+ (-1*e.movementY)+")")
+ }
+ // if(e.pointerType=='mouse'){
+ // web.sendInputEvent({type:'mouseMove', x: (e.clientX-web.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-web.getBoundingClientRect().top)/this.scatter.scale})
+ // }
+ })
+
+ overlayCaptureEvents.addEventListener('mousewheel',(e)=>{
+ // console.log("mousewheel",e.deltaY)
+ web.executeJavaScript("window.scrollTo(scrollX+"+e.deltaX+", scrollY+"+ e.deltaY+")")
+ // web.sendInputEvent({type:'mouseWheel', x: (e.clientX-web.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-web.getBoundingClientRect().top)/this.scatter.scale, deltaY: -e.deltaY})
+ })
+
+ this.webFrame.appendChild(webviewFrame)
+ webviewFrame.appendChild(webBackground)
+ webviewFrame.appendChild(shadow)
+ webviewFrame.appendChild(shadowReverse)
+ webviewFrame.appendChild(web)
+ webviewFrame.appendChild(webImage)
+ // webBackground.appendChild(heinz)
+ if(remote.getGlobal('multiUserMode'))webviewFrame.appendChild(overlayCaptureEvents)
+ webviewFrame.appendChild(overlay)
+ webviewFrame.appendChild(progressBar)
+
+ web.setAttribute('id',this.webviewID)
+ web.setAttribute('isLoading','true')
+ this.webviewMap.set(this.webviewID, webviewFrame)
+ this.webviewID++
+
+ shadow.addEventListener('pointermove',(e)=>{
+ console.log("pointer moving over shadow")
+ })
+
+ let captureFile=nodePath.join(nodeDirname, '../dev/tumortisch/var/PreviewSnapshots/phantom_'+source+"_"+this.title+'.png')
+ dummyImage.src=captureFile
+ // web.src=captureFile
+ // webImage.src=captureFile
+ web.src=newUrl
+ // console.log("newUrl",newUrl)
+ // webOffloading.src=newUrl
+ /*let zoomFactor = 1
+
+ document.body.appendChild(dummyImage)
+
+ dummyImage.addEventListener("load", ()=>{
+ let w = (dummyImage.width/dummyImage.height) * this.webFrame.offsetHeight * 2
+
+ zoomFactor=this.webFrame.offsetWidth/w
+ console.log("IMAGE LOADEED",newUrl, w,this.webFrame.offsetWidth)
+ });*/
+
+ // web.src="https://www.golem.de/"
+
+ // console.log("Appending WebView",newUrl)
+ // // document.getElementById(this.title+"bufferPadWeb").src=newUrl
+ web.preload= path.join(__dirname, './preloadPad.js')
+
+ web.addEventListener('ipc-message', (e) => {
+ if(e.channel=='webviewPointerDown')this.scatter.bringToFront()
+ })
+
+ web.addEventListener('did-fail-load',()=>{
+ console.log("failed loading",newUrl)
+ })
+ let obj = { blur: 2 }
+ let loadAnimation = new TimelineMax({repeat:-1})
+ // loadAnimation.add( TweenMax.to(obj, 0.5, {blur: 5,onUpdate: () => {web.style.filter = "blur(" + obj.blur + "px)"}}) )
+ // loadAnimation.add( TweenMax.to(obj, 0.5, {blur: 2,onUpdate: () => {web.style.filter = "blur(" + obj.blur + "px)"}}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0.5, {left:"50%"}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0, {left:"20%"}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0.5, {left:"50%"}) )
+ loadAnimation.stop()
+ // loadAnimation.add( TweenLite.to(web, 1, {filter:"blur(0px)"}) );
+
+ web.addEventListener('did-start-loading', ()=>{
+ web.setAttribute('isLoading','true')
+ loadAnimation.play()
+ overlay.style.visibility="visible"
+ progressBar.style.visibility="visible"
+ web.style.filter = "blur(10px)"
+ overlay.style.pointerEvents="none"
+
+ // web.style.filter = "blur(10px)"
+
+ // console.log('did-start-loading')
+ // loadAnim.style.visibility="visible"
+
+ let w = overlay.offsetWidth
+ let h = overlay.offsetHeight
+
+ // console.log("accordion webview did start loading",h,w)
+
+ /*let animationSize = w{
+ // web.setZoomFactor(1.0)
+
+ /*let win = new BrowserWindow({width: 800, height: 600, x: -1000, y: -1000, webPreferences: {offscreen: true}})
+ win.webContents.on('paint', (event, dirty, image) => {
+ webImage.src=image.toDataURL()
+ win.close()
+ })
+ win.loadURL(newUrl)*/
+
+ if(this.currentWebview==web)overlay.style.visibility="hidden"
+ overlay.style.pointerEvents="all"
+ // loadAnimation.stop()
+ loadAnimation.stop()
+ progressBar.style.visibility="hidden"
+ web.style.filter = "blur(0px)"
+ web.setAttribute('isLoading','false')
+ // console.log('did-stop-loading')
+ // loadAnim.style.visibility="hidden"
+ if(web==this.currentWebview){
+ // this.bufferPad.web.src=web.src
+ }
+ if(web!=this.currentWebview){
+ }
+ // if(this.webviewID{
+ // webImage.src=image.toDataURL()
+ })*/
+
+ // console.log('did-stop-loading',webImage)
+ })
+ /*web.addEventListener('did-navigate', ()=>{
+ console.log('did-navigate')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })
+ web.addEventListener('did-navigate-in-page', ()=>{
+ console.log('did-navigate-in-page')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })
+ web.addEventListener('did-frame-finish-load', ()=>{
+ console.log('did-frame-finish-load')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })
+ web.addEventListener('load-commit', ()=>{
+ console.log('load-commit')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })
+ web.addEventListener('page-title-updated', ()=>{
+ console.log('page-title-updated')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })*/
+
+
+ /*let phantom = require('phantom')
+
+ let date = new Date()
+
+ let captureFile=nodePath.join(nodeDirname, '../dev/tumortisch/var/PreviewSnapshots/phantom_'+source+"_"+this.title+'.png')
+ if (fs.existsSync(captureFile)) {
+ fs.unlink(captureFile, (err) => {
+ if (err) {
+ return
+ }
+ console.log("File succesfully deleted")
+ })
+ }
+ phantom.create().then(function(ph) {
+ ph.createPage().then(function(page) {
+ page.open(web.src).then(function(status) {
+ page.render(captureFile).then(function(){
+ webImage.src=captureFile
+ })
+ })
+ })
+ })*/
+ web.addEventListener('dom-ready', ()=>{
+
+ /*let id=this.webviewID
+ let nextURL = this.urlList[id].url
+ let nextSource = this.urlList[id].source*/
+
+ // webImage.src='var/'+source+'.png'
+ // console.log('dom-ready')
+ web.setAttribute('isLoading','false')
+ if(web==this.currentWebview){
+ // this.bufferPad.web.src=web.src
+ }
+ if(web!=this.currentWebview){
+ }
+ /* this.currentWebviewBackground = web.parentElement
+ this.currentWebview = web
+ this.currentSource = source
+ this.arrangeWebviews()*/
+
+ let captureRect = {
+ x: 0, y: 0,
+ width: 2000,
+ height: 4000
+ }
+ web.capturePage(captureRect,function(image) {
+ webImage.src=image.toDataURL()
+ // console.log("web.capturePage",newUrl)
+ let buf = image.toPNG()
+ let captureFile=nodePath.join(nodeDirname, '../dev/tumortisch/var/'+source+'.png')
+
+ fs.writeFile(captureFile, buf, function(err) {
+ if (err) {
+ console.log("ERROR Failed to save file", err)
+ }
+ else{
+ }
+ })
+ })
+ /*this.numWebviewsReady++
+ if(this.numWebviewsReady==7){
+ this.arrangeWebviews()
+ TweenMax.to(this.frame,0.0,{opacity:"1"})
+ }*/
+
+ /* web.capturePage((image)=>{
+ // webImage.src='var/cosmic.png'
+ // console.log("Captured after Load",web.src)
+ webImage.src=image.toDataURL()
+
+ /* let captureFile=nodePath.join(nodeDirname, '../dev/tumortisch/var/'+source+'.png')
+ let buf = image.toPng()
+
+ fs.writeFile(captureFile, buf, function(err) {
+ if (err) {
+ console.log("ERROR Failed to save file", err)
+ }
+ else{
+ }
+ })*/
+ //})
+ this.backButton.style.opacity = (web.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (web.canGoForward()) ? 1 : 0.5
+ // webImage.style.display="inline"
+ // if(source.indexOf("sum") >-1)web.openDevTools()
+ })
+
+ web.addEventListener('new-window', (e) => {
+ this.appendWebView(e.url)
+ // console.log("this.currentSource",source)
+ this.urlList.push({'url':e.url,'source':source})
+ })
+
+ overlay.addEventListener('click',(e)=>{
+ // console.log("Overlay wurde geklickt",this.currentWebview.src)
+ e.stopPropagation()
+ e.preventDefault()
+ this.currentWebview.capturePage((image)=>{
+ // console.log(this.webFrame.offsetHeight)
+ this.currentWebviewImage.style.height = this.webFrame.offsetHeight+"px"//"100%"
+ this.currentWebviewImage.src=image.toDataURL()
+ this.currentWebviewFrame = e.target.parentElement
+ // this.bringWebToFront()
+ e.target.style.visibility="hidden"
+
+ this.currentSource=source
+ this.expandButton.setAttribute('source',source)
+
+ this.currentWebviewFrame = e.target.parentElement
+ this.currentWebview = web
+ this.currentSource = source
+ this.currentWebviewImage = webImage
+ this.arrangeWebviews()
+ // this.bufferPad.web.src=web.src
+
+ this.backButton.style.opacity = (web.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (web.canGoForward()) ? 1 : 0.5
+ })
+ //e.target.parentElement.style.zIndex=this.zIndex
+
+ })
+ /*overlay.addEventListener('click',(e)=>{
+ e.stopPropagation()
+ e.preventDefault()
+ this.currentWebviewBackground = e.target.parentElement
+ this.bringWebToFront()
+ e.target.style.visibility="hidden"
+ //e.target.parentElement.style.zIndex=this.zIndex
+ })*/
+
+ webImage.addEventListener('pointerenter',(e)=>{
+ // TweenMax.to(webBackground, 0.1, {x:"+= 50"})
+ })
+
+ webImage.addEventListener('pointerleave',(e)=>{
+ // TweenMax.to(webBackground, 0.1, {x:"-= 50"})
+ })
+
+ webImage.addEventListener('click',(e)=>{
+
+ this.currentSource=source
+ this.expandButton.setAttribute('source',source)
+
+ // this.expandButton.style.opacity="0.5"
+ e.stopPropagation()
+ e.preventDefault()
+ let oldWeb=this.currentWebview
+ let oldWebImage = this.currentWebviewImage
+
+ oldWeb.capturePage((image)=>{
+ oldWebImage.src=image.toDataURL()
+
+ this.currentWebviewBackground = e.target.parentElement
+ this.currentWebview = web
+ this.currentWebviewImage = webImage
+ this.currentSource = source
+ this.arrangeWebviews()
+ })
+
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+
+ /* let captureFile=nodePath.join(nodeDirname, '../dev/tumortisch/var/'+source+'.png')
+ let buf = image.toPNG()
+
+ fs.writeFile(captureFile, buf, function(err) {
+ if (err) {
+ console.log("ERROR Failed to save file", err)
+ }
+ else{
+ }
+ })*/
+ })
+
+ // this.bufferPad.web.src=web.src
+
+ this.backButton.style.opacity = (web.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (web.canGoForward()) ? 1 : 0.5
+ // e.target.style.visibility="hidden"
+ //e.target.parentElement.style.zIndex=this.zIndex
+ })
+
+ /*overlay.addEventListener('pointerenter',(e)=>{
+ TweenMax.to(webBackground, 0.1, {x:"+= 20"})
+ })
+
+ overlay.addEventListener('pointerleave',(e)=>{
+ TweenMax.to(webBackground, 0.1, {x:"-= 20"})
+ })*/
+ this.arrangeWebviews()
+ }
+
+ arrangeWebviews(){
+ let numWebviews = this.webviewMap.size
+ let index = 0
+ let increment = 1
+ let z = this.zIndex
+ let current=this.currentWebviewFrame
+ let shadowWidth = 0
+ this.webviewMap.forEach(function (entry, key) {
+ entry.style.maxWidth="1000px"
+ // entry.style.pointerEvents = "none"
+ // entry.style.width=100-Math.sqrt(entry.parentElement.offsetWidth-590)+"%"
+ entry.style.width="70%"
+ shadowWidth = $(entry).find(".accordionWebviewShadow").outerWidth()
+ // console.log("entry.parentElement.offsetWidth",entry.parentElement.offsetWidth)
+ z+=increment
+ let offset=index*(entry.parentElement.offsetWidth+2*shadowWidth-entry.offsetWidth)/(numWebviews-1)
+ // console.log("Offset",entry.parentElement.offsetWidth-(offset+entry.offsetWidth))
+ entry.style.left=entry.parentElement.offsetWidth+2*shadowWidth-(offset+entry.offsetWidth)-shadowWidth+"px"
+ entry.style.zIndex=z
+ $( entry ).children(".accordionWebview[isLoading='false']").css( "visibility", "hidden" )
+ // $( entry ).children(".accordionWebview").css( "visibility", "hidden" )
+ /* $( entry ).children(".accordionWebview").css( "visibility", "hidden" ).each(function(i, obj) {
+ console.log("isLoading()",obj.isLoading())
+ })*/
+ $( entry ).children('.accordionOverlay').css( "visibility", "visible" )
+ $( entry ).children('.accordionWebimage').css( "visibility", "visible" )
+ // entry.style.left=index*(entry.parentElement.offsetWidth-entry.offsetWidth)/(numWebviews-1)+"px"
+ index++
+ if(entry==current){
+ $( entry ).children('.accordionWebview').css( "visibility", "visible" )
+ $( entry ).children('.accordionOverlay').css( "visibility", "hidden" )
+ $( entry ).children('.accordionWebimage').css( "visibility", "hidden" )
+ increment*=-1
+ // entry.childNodes[1].style.opacity=0.8
+ }
+ })
+ this.zIndex=z
+ // this.currentWebviewFrame.style.pointerEvents = "all"
+
+ // this.enableButtons()
+ /*this.zIndex++
+ this.currentWebviewBackground.style.zIndex=this.zIndex
+ $( this.currentWebviewBackground ).children('.accordionWebview').css( "visibility", "visible" )
+ $( this.currentWebviewBackground ).children('.accordionOverlay').css( "visibility", "hidden" )
+ $( this.currentWebviewBackground ).children('.accordionWebimage').css( "visibility", "hidden" )*/
+}
+
+ bringWebToFront(){
+ this.arrangeWebviews()
+ this.zIndex++
+ this.currentWebviewFrame.style.zIndex=this.zIndex
+ // this.currentWebviewBackground.childNodes[2].style.opacity=0.8
+ }
+ rad2degree(alpha){
+ return alpha * 180 / Math.PI;
+ }
+
+ degree2rad(alpha){
+ return alpha * Math.PI / 180;
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ urlPadMap.delete(this.url)
+ urlPadMap.delete(this.title)
+ let geneTab = document.getElementById(this.title +"_tab")
+ TweenMax.to(geneTab, 0.4, {scaleX:"1.0",x:"0px"});
+ geneTab.style.boxShadow = "none"
+ geneTab.setAttribute('open','false')
+ }
+
+ enableButtons() {
+ this.backButton.style.opacity = (this.currentWebview.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (this.currentWebview.canGoForward()) ? 1 : 0.5
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value=value
+ button.draggable = false
+ button.classList.add("interactiveElement")
+ this.frame.appendChild(button)
+ return button
+ }
+
+ layout() {
+ // console.log("LAYOUT")
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+ let size = "calc(100% - " + (2*b+2) +"px)"
+ let h = "calc(100% - " + (b) +"px)"
+ let w = "calc(100% - "+2 +"px)"
+
+ this.scatter.resizeButton.style.width=b+"px"
+ this.scatter.resizeButton.style.height=b+"px"
+
+ Elements.setStyle(this.webFrame, {
+ width: w,
+ height: h,
+ margin: "1px"})
+ /*Elements.setStyle(this.webBackground, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+
+ Elements.setStyle(this.overlay, {
+ width: size,
+ height: size,
+ margin: b+"px"})*/
+
+ Elements.setStyle(this.titlebar, {
+ left: (b * 5.75) + "px",
+ bottom: "0px",
+ fontSize: 0.7*b+"px",
+ // paddingTop: 10+"px",
+ // textAlign: "center",
+ margin: 0.1*b+"px",
+ height: 0.8*b+"px"})
+ Elements.setStyle(this.closeAllButton, {
+ right: (b * 1.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.minimizeButton, {
+ right: (b * 3.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.maximizeButton, {
+ right: (b * 3.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.backButton, {
+ left: (b * 0.8) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.forwardButton, {
+ left: (this.border + (b * 0.8)) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.expandButton, {
+ left: (this.border + (b * 1.8)) +"px",
+ bottom: "0px",
+ // opacity: "0.5",
+ margin: 0.1*b+"px",
+ width: 0.8*b+"px",
+ height: 0.8*b+"px"})
+ Elements.setStyle(this.closeButton, {
+ left: (this.border + (b * 2.8)) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ }
+
+}
+
+class PadFromElement {
+
+ constructor(element, scatterContainer, {
+ startScale=1.0, minScale=0.1, maxScale=1.0,
+ autoBringToFront=true,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ type = 'fromElement',
+ title = 'New Pad',
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ this.element = element
+ this.type = type
+ this.title = title
+ this.x = x
+ this.y = y
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.frame = document.createElement('div')
+ Elements.setStyle(this.frame, {
+ width: this.width+"px",
+ height: this.height+"px",
+ backgroundColor: "#333",
+ position: "fixed",
+ visibility: "visible",
+ top: 0,
+ left: 0,
+ overflow: "auto"})
+
+ this.closeButton = this.addButton("../assets/icons/svg/cross.svg", "close")
+
+ document.body.appendChild( this.frame)
+ this.border = 50
+
+ this.frame.appendChild(this.element)
+
+ this.title = document.createElement("div")
+ this.title.innerHTML = "Titel"
+ this.title.style.color = "white"
+ this.frame.appendChild(this.title)
+
+ Elements.setStyle(this.title, {
+ position: "absolute"
+ })
+ // this.element.style.overflow = "auto"
+ // this.element.style.position = "absolute"
+
+ this.layout()
+
+ /*this.closeButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.frame.style.visibility="hidden"
+ })*/
+
+ InteractionMapper.on('tap',this.closeButton, e => {
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.frame.style.visibility="hidden"
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ this.scatter.bringToFront()
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ this.element.addEventListener('pointerdown', (e) => {
+ this.scatter.bringToFront()
+ })
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ // urlPadMap.delete(this.url)
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value=value
+ button.draggable = false
+ button.classList.add("interactiveElement")
+ this.frame.appendChild(button)
+ return button
+ }
+
+ layout() {
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+ let size = "calc(100% - " + (2*b+2) +"px)"
+ Elements.setStyle(this.element, {
+ width: size,
+ height: size,
+ top: b+"px",
+ left: b+"px"})
+ Elements.setStyle(this.closeButton, {
+ right: (b * 0.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.title, {
+ left: (b * 1.5) + "px",
+ fontSize: (b * 0.8) + "px",
+ top: (0.1)+"0px"})
+ }
+}
+class minimalPad {
+
+ constructor(scatterContainer, {
+ startScale=1.0, minScale=0.25, maxScale=10.5,
+ autoBringToFront=true,
+ type = 'minimal',
+ title="new Pad",
+ urlList=["https://www.iwm-tuebingen.de/www/index.html"],
+ padMapKey="key",
+ // url="https://www.iwm-tuebingen.de/www/index.html",
+ hideOnStart=false,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ minimizedHiddenHeight = 0,
+ minimizedHiddenWidth = 0,
+ minimized = false,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ let w = window,
+ d = document,
+ e = d.documentElement,
+ g = d.getElementsByTagName('body')[0]
+ this.pageWidth = w.innerWidth || e.clientWidth || g.clientWidth,
+ this.pageHeight = w.innerHeight|| e.clientHeight|| g.clientHeight
+
+ this.x = x
+ this.y = y
+ this.type = type
+ // this.url = url
+ this.urlList = urlList
+ this.padMapKey = padMapKey,
+ this.title = title,
+ this.hideOnStart = hideOnStart
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.startScale = startScale
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+ this.minimizedHiddenHeight = minimizedHiddenHeight
+ this.minimizedHiddenWidth = minimizedHiddenWidth
+ this.minimized = minimized
+
+ this.web = null
+ this.WebviewCounter = 0
+ this.id=0
+
+ this.webviewMap = new Map()
+ this.overlayMap = new Map()
+ this.tabMap = new Map()
+ this.listMap = new Map()
+
+ this.frame = document.createElement('div')
+ this.frame.classList.add("pad")
+ this.border = this.pageHeight*0.023148 / startScale
+
+ Elements.setStyle(this.frame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ display: 'flex',
+ width: this.width+"px",
+ height: this.height+"px",
+ visibility: "visible",
+ top: 0,
+ left: 0,
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ overflow: "visible"})
+
+ document.body.appendChild( this.frame)
+
+ this.tabs=document.createElement("div")
+ this.webBackground=document.createElement("div")
+ this.webviewList=document.createElement("div")
+ this.titlebar = document.createElement("div")
+ this.webViewSnapshot=document.createElement("img")
+
+ this.tabs.classList.add("interactiveElement")
+ this.tabs.classList.add("tabs")
+ // this.tabs.style.display="flex"
+ // this.tabs.style.flexFlow="row nowrap"
+
+ Elements.setStyle(this.tabs, {
+ position: "absolute",
+ background: "white",
+ overflowX: "auto",
+ overflowY: "hidden",
+ display: "flex",
+ flexFlow:"row nowrap",
+ // justifyContent: "flex-end",
+ alignItems: "flex-end"
+ // overflow: "auto",
+ })
+
+ this.titlebar.innerHTML = this.title
+ Elements.setStyle(this.titlebar, {
+ color: "#fff",
+ position: "absolute",
+ overflowX: "hidden"
+ })
+
+ Elements.setStyle(this.webBackground, {
+ position: "absolute",
+ overflow: "hidden",
+ background: "white"
+ })
+
+ this.webviewList.classList.add("interactiveElement")
+
+ Elements.setStyle(this.webviewList, {
+ position: "absolute",
+ visibility: "hidden",
+ overflow: "auto",
+ background: "white",
+ boxShadow: "5px 5px 10px #bbb"
+ })
+
+ /*Elements.setStyle(this.overlay, {
+ position: "absolute",
+ overflow: "auto",
+ background: "white",
+ opacity: "0.8"
+ })*/
+
+ Elements.setStyle(this.webViewSnapshot, {
+ position: "absolute",
+ overflow: "auto"
+ })
+
+ this.frame.appendChild(this.titlebar)
+ this.frame.appendChild(this.webBackground)
+ // this.frame.appendChild(this.webViewSnapshot)
+ this.frame.appendChild(this.tabs)
+ this.frame.appendChild(this.webviewList)
+ this.showWebviewListButton = this.addButton("../dev/tumortisch/icons/ic_list_48px.svg", "show list")
+ this.expandButton = this.addButton("../dev/tumortisch/icons/launch.png", "expand")
+ this.expandButton.id=this.title+"expandButton"
+
+ this.showWebviewListButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.webviewList.style.visibility=="visible" ? this.webviewList.style.visibility="hidden" : this.webviewList.style.visibility="visible"
+ })
+
+ this.expandButton.addEventListener('click',(e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.externalPadID++
+ let newPadKey=this.title+this.externalPadID
+ // console.log("NEW PAD KEY",newPadKey)
+ // console.log("expanding",this.currentWebview.getTitle())
+ // console.log("expanding",this.currentWebview.getURL())
+
+ let newPad= new minimalPad(nodePadContainer, {
+ x: 500,
+ y: 200,
+ padMapKey: newPadKey,
+ title: this.title+" - "+ this.expandButton.getAttribute('source'),//this.currentWebview.getTitle(),
+ urlList: [e.target.getAttribute('url')],
+ startScale:1,
+ hideOnStart: false,
+ width: this.pageWidth*0.3906,
+ height: this.pageWidth*0.3906,
+ scalable: true,
+ rotatable: false})
+
+ urlPadMap.set(newPadKey,newPad)
+ })
+
+ for(var i=0; i{
+ this.scatter.scalable=false
+ this.web.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameTop.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameLeft.addEventListener('pointerenter',(e)=>{
+ // e.target.setPointerCapture(e.pointerId)
+ this.scatter.scalable=false
+ this.web.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameLeft.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameRight.addEventListener('pointerenter',(e)=>{
+ this.scatter.scalable=false
+ this.web.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameRight.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ /*this.web.addEventListener('new-window', (e) => {
+
+ if(e.url.indexOf("youtube")>-1)return
+
+ if (urlPadMap.has(e.url)) {
+ let childPad = urlPadMap.get(e.url)
+ childPad.scatter.moveTo(x, y)
+ return childPad
+ }
+ let childPad = new minimalPad(this.scatterContainer, {
+ x: this.scatter.position.x+100,
+ y: this.scatter.position.y+100,
+ url: e.url,
+ width: this.scatter.width,
+ height: this.scatter.height,
+ scalable: true,
+ rotatable: true})
+ urlPadMap.set(e.url, childPad)
+
+ for(let callback of window.padLoadedHandler) {
+ callback(childPad, url)
+ }
+ })*/
+ InteractionMapper.on('tap',this.minimizeButton, e => {
+ let s = this.scatter.scale
+ let x=0
+ let y=0
+ let deltaX = 0
+ let deltaY = 0
+ switch(this.minimized){
+ case false:
+
+ let w = this.frame.offsetWidth*s < 800 ? this.frame.offsetWidth : 800/s
+
+ x = this.frame.getBoundingClientRect().right - w
+ y = this.frame.getBoundingClientRect().bottom - this.border
+
+ deltaX = this.frame.offsetWidth - w
+ deltaY = this.frame.offsetHeight - this.border
+
+ this.minimizedHiddenHeight = this.frame.offsetHeight
+ this.minimizedHiddenWidth = this.frame.offsetWidth
+
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+
+ this.minimize(w, this.border, this.scatter.scale, deltaX*s, deltaY*s)
+ this.minimized = true
+ this.minimizeButton.src = fileURL("../dev/tumortisch/icons/fullscreen.png")
+ this.scatter.scalable = false
+ $(this.frame).children(".frameButton").each(function(i, obj) {
+ if(obj.value !="close" && obj.value !="closeAll" && obj.value !="minimize")obj.style.pointerEvents = "none"
+ })
+ // this.frame.style.pointerEvents = "none"
+ // this.minimizeButton.style.pointerEvents = "all"
+ // this.closeButton.style.pointerEvents = "all"
+ break
+ case true:
+
+ s = this.scatter.scale
+
+ x = this.frame.getBoundingClientRect().right - this.minimizedHiddenWidth
+ y = this.frame.getBoundingClientRect().bottom - this.minimizedHiddenHeight
+
+ deltaX = this.frame.getBoundingClientRect().width - this.minimizedHiddenWidth*s
+ deltaY = this.frame.getBoundingClientRect().height - this.minimizedHiddenHeight*s
+
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+
+ this.minimize(this.minimizedHiddenWidth, this.minimizedHiddenHeight, this.scatter.scale, deltaX, deltaY)
+ this.minimized = false
+ this.minimizeButton.src = fileURL("../dev/tumortisch/icons/remove.png")
+ this.scatter.scalable = true
+ $(this.frame).children(".frameButton").each(function(i, obj) {
+ obj.style.pointerEvents = "all"
+ })
+ // this.frame.style.pointerEvents = "all"
+ break
+ }
+ })
+
+ InteractionMapper.on('tap',this.backButton, e => {
+ if(this.web.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goBack()
+ }
+ })
+
+ InteractionMapper.on('tap',this.forwardButton, e => {
+ if(this.web.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goForward()
+ }
+ })
+
+ InteractionMapper.on('tap',this.closeButton, e => {
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })
+
+ /* this.backButton.addEventListener('click', (e)=>{
+ if(this.web.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goBack()
+ }
+ })
+
+ this.forwardButton.addEventListener('click', (e)=>{
+ if(this.web.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goForward()
+ }
+ })
+
+ this.closeButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })*/
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "./icons/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+ // this.scatter.resizeButton.className = "frameButton"
+
+ this.scatter.moveTo({x, y})
+ this.scatter.bringToFront()
+
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = this.pageHeight*0.023148 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ this.layout()
+
+ /*this.test = document.createElement('div')
+
+ Elements.setStyle(this.test, {
+ position: "absolute",
+ width: "400px",
+ height: "400px",
+ overflow: "auto",
+ background: "pink"
+ })
+ this.frame.appendChild(this.test)
+
+ this.test.addEventListener('click',()=>{
+ // this.frame.style.visibility="hidden"
+ console.log($( this.webBackground ).children())
+ $( this.webBackground ).children().css( "visibility", "hidden" )
+ $( this.frame ).children().css( "visibility", "hidden" )
+ })*/
+
+ }
+
+ minimize(width, height, scale, x, y) {
+
+ let oldPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+
+ this.frame.style.transformOrigin = "0% 0%"
+
+ let newPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+
+ let offset = Points.subtract(oldPostition, newPostition)
+
+ TweenLite.to(this.frame, 0, { css: { left: "+=" + offset.x + "px" } })
+ TweenLite.to(this.frame, 0, { css: { top: "+=" + offset.y + "px" } })
+
+ // TweenLite.to(this.frame, 0, { width: width , height: height, x: x, y: y })
+ TweenLite.to(this.frame, 0, { width: width , height: height})
+ TweenLite.to(this.frame, 0, { css: { left: "+=" + x + "px" } })
+ TweenLite.to(this.frame, 0, { css: { top: "+=" + y + "px" } })
+
+ oldPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+ this.frame.style.transformOrigin = "50% 50%"
+ newPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+ offset = Points.subtract(oldPostition, newPostition)
+
+ TweenLite.to(this.frame, 0, { css: { left: "+=" + offset.x + "px" } })
+ TweenLite.to(this.frame, 0, { css: { top: "+=" + offset.y + "px" } })
+
+ }
+
+ addNewWebview(src){
+ /* if(this.web!=null){
+ this.web.style.visibility="hidden"
+ this.web.setAttribute('selected','0')
+ }*/
+
+ let webview=document.createElement("webview")
+ let progressBar = document.createElement('div')
+ webview.setAttribute("plugins",true)
+ this.web=webview
+ webview.setAttribute('loaded','0')
+ this.webBackground.appendChild(webview)
+ this.webviewMap.set(src+this.padMapKey, webview)
+ // console.log("number of webviews",this.webviewMap.size)
+ // $("webview").each(function(i, obj) {
+ // obj.setAttribute('selected','0')
+ // })
+
+ Elements.setStyle(webview, {
+ position: "absolute",
+ overflow: "auto",
+ width: "100%",
+ height: "100%",
+ // border: "1px solid #fff"
+ })
+
+ Elements.setStyle(progressBar, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0%",
+ left: "10%",
+ visibility: "hidden",
+ background: "white",
+ filter: "blur(50px)",
+ opacity: "0.2",
+ // pointerEvents: 'none',
+ width: "40%",
+ height: "100%"
+ // opacity: "0.1"
+ })
+
+ webview.src=src
+ webview.preload= path.join(__dirname, './preloadPad.js')
+
+ let listItem=document.createElement('div')
+ listItem.className="interactiveElement"
+ listItem.classList.add("webviewListItem")
+ listItem.setAttribute('id',src+this.padMapKey)
+ // listItem.style.height = "5%"
+ // // listItem.style.width = "80%"
+ // listItem.style.marginLeft="10%"
+ // listItem.style.marginTop="5%"
+ listItem.style.padding = "10px"
+ // listItem.style.paddingBottom = "10px"
+ this.webviewList.appendChild(listItem)
+ this.listMap.set(src+this.padMapKey,listItem)
+
+ webview.setAttribute('id',src+this.padMapKey)
+
+ this.setSelectedWebview(src+this.padMapKey)
+
+ this.addNewTab(webview,src)
+
+ listItem.addEventListener('click',(e)=>{
+ console.log(e.target.getAttribute('id'))
+ this.setSelectedWebview(e.target.getAttribute('id'))
+ this.webviewList.style.visibility = "hidden"
+ $(this.tabs).animate({scrollLeft: $(".tab[id='"+e.target.getAttribute('id')+"']").prop('offsetLeft') - 10}, "slow")
+ this.enableButtons()
+ })
+
+ listItem.addEventListener('pointerenter',(e)=>{
+ e.target.style.background = "#ddd"
+ let webview=null
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ if(item.getAttribute('selected')=='0')item.style.opacity="0.5"
+ webview=item
+ // obj.style.width="50%"
+ // obj.style.height="50%"
+ }
+ })
+ this.overlayMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ }
+ })
+ // webview.getAttribute('loaded')=='0' ? this.overlay.style.visibility='visible' : this.overlay.style.visibility='hidden'
+ })
+
+ listItem.addEventListener('pointerleave',(e)=>{
+ e.target.style.background = "#fff"
+ if(e.target.getAttribute('selected')=='1') e.target.style.background = "#999"
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ if(item.getAttribute('selected')=='0'){
+ item.style.visibility="hidden"
+ }
+ if(item.getAttribute('selected')=='1'){
+ item.style.visibility="visible"
+ item.style.opacity="1"
+ }
+ // obj.style.width="100%"
+ // obj.style.height="100%"
+ })
+ })
+
+ let overlay = document.createElement('img')
+ overlay.style.position="absolute"
+ overlay.style.width="100%"
+ overlay.style.height="100%"
+ overlay.style.top="0px"
+ overlay.style.left="0px"
+ // overlay.style.background="white"
+ overlay.classList.add("minimalPadOverlay")
+ overlay.src="img/overlay.png"
+
+ this.overlayMap.set(src,overlay)
+
+ let timeTouchStart = 0
+
+ let overlayCaptureEvents = document.createElement('div')
+ overlayCaptureEvents.style.position="absolute"
+ overlayCaptureEvents.style.background="white"
+ overlayCaptureEvents.style.opacity="0.01"
+ overlayCaptureEvents.style.width="100%"
+ overlayCaptureEvents.style.height="100%"
+ overlayCaptureEvents.style.top="0px"
+ overlayCaptureEvents.style.left="0px"
+ // overlay.style.background="white"
+
+ overlayCaptureEvents.classList.add("interactiveElement")
+
+ overlayCaptureEvents.addEventListener('touchmove',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+ })
+
+ overlayCaptureEvents.addEventListener('pointerup',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+
+ let d = new Date()
+ console.log("delta Time",d.getTime()-timeTouchStart)
+ if(d.getTime()-timeTouchStart<150)web.sendInputEvent({type:'mouseUp', x: (e.clientX-web.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-web.getBoundingClientRect().top)/this.scatter.scale, button:'left', clickCount: 1})
+ })
+
+ overlayCaptureEvents.addEventListener('pointerdown',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+
+ let d = new Date()
+ timeTouchStart = d.getTime()
+ console.log("timeTouchStart",timeTouchStart)
+ web.sendInputEvent({type:'mouseDown', x: (e.clientX-web.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-web.getBoundingClientRect().top)/this.scatter.scale, button:'left', clickCount: 1})
+ })
+
+ overlayCaptureEvents.addEventListener('pointermove',(e)=>{
+ if(e.pointerType!='mouse'){
+ // console.log("e.movementY",e.movementY)
+ web.executeJavaScript("window.scrollTo(scrollX+"+(-1*e.movementX)+", scrollY+"+ (-1*e.movementY)+")")
+ }
+ // if(e.pointerType=='mouse'){
+ // web.sendInputEvent({type:'mouseMove', x: (e.clientX-web.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-web.getBoundingClientRect().top)/this.scatter.scale})
+ // }
+ })
+
+ overlayCaptureEvents.addEventListener('mousewheel',(e)=>{
+ // console.log("mousewheel",e.deltaY)
+ web.executeJavaScript("window.scrollTo(scrollX+"+e.deltaX+", scrollY+"+ e.deltaY+")")
+ // web.sendInputEvent({type:'mouseWheel', x: (e.clientX-web.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-web.getBoundingClientRect().top)/this.scatter.scale, deltaY: -e.deltaY})
+ })
+
+ let loadAnim = document.createElement("div")
+ loadAnim.style.webkitAnimation= "spin 2s linear infinite"
+ loadAnim.style.animation= "spin 2s linear infinite"
+ loadAnim.style.position = "absolute"
+
+ document.styleSheets[0].insertRule("div.tabs::-webkit-scrollbar {display: none;}");
+ document.styleSheets[0].insertRule('\
+ @keyframes spin {\
+ from { transform: rotateZ(0deg); }\
+ to { transform: rotateZ(360deg); }\
+ }'
+ )
+
+ // overlay.appendChild(loadAnim)
+
+ if(remote.getGlobal('multiUserMode'))this.webBackground.appendChild(overlayCaptureEvents)
+ this.webBackground.appendChild(overlay)
+ this.webBackground.appendChild(progressBar)
+
+ webview.addEventListener('did-navigate', (e) => {
+ this.enableButtons()
+ //this.backButton.style.opacity = (webview.canGoBack()) ? 1 : 0.5
+ //this.forwardButton.style.opacity = (webview.canGoForward()) ? 1 : 0.5
+ })
+
+ webview.addEventListener('dom-ready',()=>{
+ // console.log("DOM READY TABBED WEBVIEW!!!!!!!!!!")
+ listItem.innerHTML=webview.getTitle()+" "+src
+ $(".tab[id='"+src+this.padMapKey+"']").children()[0].innerHTML=webview.getTitle()
+ // $(".tab[id='"+src+"']").children()[0].innerHTML="READY LOADED"
+ $(this.tabs).animate({scrollLeft: $(".tab[id='"+src+"']").prop('offsetLeft') - 10}, "slow")
+
+ })
+
+ webview.addEventListener('ipc-message', (e) => {
+ if(e.channel=='webviewPointerDown')this.scatter.bringToFront()
+ })
+
+ let obj = { blur: 2 }
+ let loadAnimation = new TimelineMax({repeat:-1})
+ // loadAnimation.add( TweenMax.to(obj, 0.5, {blur: 5,onUpdate: () => {web.style.filter = "blur(" + obj.blur + "px)"}}) )
+ // loadAnimation.add( TweenMax.to(obj, 0.5, {blur: 2,onUpdate: () => {web.style.filter = "blur(" + obj.blur + "px)"}}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0.5, {left:"60%"}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0, {left:"20%"}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0.5, {left:"60%"}) )
+ loadAnimation.stop()
+
+ webview.addEventListener('did-start-loading', ()=>{
+ console.log("started loading page")
+
+ webview.setAttribute('loaded','0')
+ overlay.style.visibility="visible"
+ webview.style.filter = "blur(10px)"
+ progressBar.style.visibility="visible"
+ loadAnimation.play()
+
+ let w = overlay.offsetWidth
+ let h = overlay.offsetHeight
+
+ /*let animationSize = w{
+ loadAnimation.stop()
+ webview.style.filter = "blur(0px)"
+ webview.setAttribute('loaded','1')
+ this.layout()
+ progressBar.style.visibility="hidden"
+ overlay.style.visibility="hidden"
+ })
+
+ webview.addEventListener('new-window', (e) => {
+ if (!this.webviewMap.has(e.url+this.padMapKey)){
+ this.addNewWebview(e.url)
+ }
+ })
+ this.id++
+ }
+
+ setSelectedWebview(id){
+ let webview=null
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ item.setAttribute('selected','0')
+ item.style.opacity = "0"
+ item.style.visibility = "hidden"
+ if(key==id){
+ webview=item
+ item.setAttribute('selected','1')
+ item.style.opacity = "1"
+ item.style.visibility = "visible"
+ }
+ })
+
+ this.tabMap.forEach(function (tab, key, mapObj) {
+ tab.style.border="none"
+ if(key==id){
+ tab.style.borderLeft="1px solid black"
+ tab.style.borderTop="1px solid black"
+ tab.style.borderRight="1px solid black"
+ }
+ })
+
+ this.listMap.forEach(function (entry, key, mapObj) {
+ entry.style.background = "white"
+ entry.setAttribute('selected','0')
+ if(key==id){
+ entry.style.background = "#999"
+ entry.setAttribute('selected','1')
+ }
+ })
+ this.web = webview
+ // webview.getAttribute('loaded')=='0' ? this.overlay.style.visibility='visible' : this.overlay.style.visibility='hidden'
+ }
+
+ addNewTab(webview,src){
+
+ this.tabMap.forEach(function (tab, key, mapObj) {
+ tab.style.border="none"
+ })
+ let tab=document.createElement('div')
+ tab.className="tab"
+ tab.classList.add("interactiveElement")
+ tab.style.display="flex"
+ tab.style.flexFlow="row nowrap"
+ tab.style.textOverflow="ellipsis"
+ tab.setAttribute('id',src+this.padMapKey)
+ // tab.innerHTML=" "
+ tab.style.color="black"
+ tab.style.background="white"
+ tab.style.borderLeft="1px solid black"
+ tab.style.borderTop="1px solid black"
+ tab.style.borderRight="1px solid black"
+ tab.style.position="relative"
+ tab.style.borderRadius="5px 5px 0 0"
+ tab.style.maxWidth="25%"
+ tab.style.height="80%"
+ tab.style.alignItems= "center"
+ tab.setAttribute('source',src)
+ // tab.style.display="inline"
+ // tab.style.marginLeft="10px"
+ tab.style.paddingLeft="10px"
+ this.tabs.appendChild(tab)
+ this.tabMap.set(src+this.padMapKey,tab)
+ let newSelectedWebview=null
+
+ let title = document.createElement('div')
+ title.classList.add('tabTitle')
+ // title.style.display="flex"
+ // title.style.flexFlow="row nowrap"
+ title.style.overflow="hidden"
+ title.style.whiteSpace="nowrap"
+ title.style.textOverflow="ellipsis"
+ title.style.pointerEvents="none"
+ title.innerHTML="new Tab"
+ this.expandButton.setAttribute('url',src)
+ tab.appendChild(title)
+
+ let close = document.createElement('img')
+ close.setAttribute('id',src+this.padMapKey)
+ close.classList.add('tabCloseButton')
+ close.style.background="#666"
+ close.style.height="50%"
+ close.style.marginLeft="10px"
+ close.style.marginRight="10px"
+ close.style.borderRadius="100%"
+ close.style.visibility="hidden"
+ close.src = "../../assets/icons/svg/cross.svg"
+ tab.appendChild(close)
+
+ close.addEventListener('click',(e)=>{
+ e.stopPropagation()
+ e.preventDefault()
+ let newSelectedItem = false
+
+ if(this.webviewMap.size>1){
+ let next=this.getNextTab(e.target.getAttribute("id"))
+ let previous=this.getPreviousTab(e.target.getAttribute("id"))
+
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ if(item.getAttribute('selected')=='1'){
+ newSelectedItem=true
+ }
+ mapObj.delete(key)
+ $(item).remove()
+ }
+ })
+
+ this.tabMap.forEach(function (tab, key, mapObj) {
+ if(tab.getAttribute('id')==e.target.getAttribute("id")){
+ mapObj.delete(key)
+ $(tab).remove()
+ }
+ })
+
+ this.listMap.forEach(function (entry, key, mapObj) {
+ if(entry.getAttribute('id')==e.target.getAttribute("id")){
+ mapObj.delete(key)
+ }
+ })
+ $(".webviewListItem[id='"+e.target.getAttribute("id")+"']").remove()
+ $(e.target).remove()
+ if(newSelectedItem){
+ next!=null ? this.setSelectedWebview(next.getAttribute('id')) : this.setSelectedWebview(previous.getAttribute('id'))
+ }
+ }
+ })
+
+ tab.addEventListener('pointerenter',(e)=>{
+ if(this.webviewMap.size>1)e.target.childNodes[1].style.visibility="visible"
+ let z=this.frame.style.zIndex
+
+ let webview=null
+ e.target.style.background="#ddd"
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ if(item.getAttribute('selected')=='0')item.style.opacity="0.5"
+ webview=item
+ // obj.style.width="50%"
+ // obj.style.height="50%"
+ }
+ })
+ this.overlayMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ }
+ })
+ // webview.getAttribute('loaded')=='0' ? this.overlay.style.visibility='visible' : this.overlay.style.visibility='hidden'
+ /*Popup.open({"text":webview.getTitle()},
+ {x: e.clientX, y: e.clientY},
+ { fontSize: "2vh", maxWidth: width*0.2, spacing: '10px', notchPosition: 'topLeft', zIndex: z+100})*/
+ })
+
+ tab.addEventListener('pointerleave',(e)=>{
+ e.target.childNodes[1].style.visibility="hidden"
+ e.target.style.background="#fff"
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ if(item.getAttribute('selected')=='0'){
+ item.style.visibility="hidden"
+ }
+ if(item.getAttribute('selected')=='1'){
+ item.style.visibility="visible"
+ item.style.opacity="1"
+ }
+ // obj.style.width="100%"
+ // obj.style.height="100%"
+ })
+ //this.web.style.visibility="visible"
+ //this.web.style.opacity="1"
+ })
+ tab.addEventListener('click',(e)=>{
+ this.tabMap.forEach(function (item, key, mapObj) {
+ item.style.border="none"
+ })
+
+ e.target.style.borderLeft="1px solid black"
+ e.target.style.borderTop="1px solid black"
+ e.target.style.borderRight="1px solid black"
+
+ this.setSelectedWebview(e.target.getAttribute('id'))
+ this.expandButton.setAttribute("url",e.target.getAttribute('source'))
+
+ if(e.target.offsetLeft+e.target.offsetWidth>this.tabs.offsetWidth+this.tabs.scrollLeft){
+ $(this.tabs).animate({scrollLeft: e.target.offsetLeft-this.tabs.offsetWidth+e.target.offsetWidth}, "slow")
+ }
+ if(e.target.offsetLeft 1){
+ this.tabs.style.display = "flex"
+ this.showWebviewListButton.style.display = "inline"
+
+ if(this.hasTtitleBar){
+ Elements.setStyle(this.webBackground, {
+ top: b+"px",
+ width: w,
+ height: size-b,
+ margin: "1px"})
+ Elements.setStyle(this.titlebar, {
+ display: "inline"})
+ }
+ if(!this.hasTtitleBar){
+ Elements.setStyle(this.webBackground, {
+ width: w,
+ height: size,
+ margin: "1px"})
+ }
+ }
+ if(this.tabMap.size <= 1){
+ this.tabs.style.display = "none"
+ this.showWebviewListButton.style.display = "none"
+
+ if(this.hasTtitleBar){
+ Elements.setStyle(this.webBackground, {
+ top: b+"px",
+ width: w,
+ height: size,
+ margin: "1px"})
+ Elements.setStyle(this.titlebar, {
+ display: "inline"})
+ }
+ if(!this.hasTtitleBar){
+ Elements.setStyle(this.webBackground, {
+ width: w,
+ height: h,
+ margin: "1px"})
+ }
+ }
+
+ }
+
+}
+
+class minimalPadFromElement {
+
+ constructor(element, scatterContainer, {
+ startScale=1.0, minScale=0.1, maxScale=1.0,
+ autoBringToFront=true,
+ type = 'minimalFromElement',
+ title = 'new Pad',
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ minimizedHiddenHeight = 0,
+ minimizedHiddenWidth = 0,
+ minimized = false,
+ resizable=false,
+ } ={}) {
+
+ this.element = element
+ this.x = x
+ this.y = y
+ this.type = type
+ this.title = title
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+ this.minimizedHiddenHeight = minimizedHiddenHeight
+ this.minimizedHiddenWidth = minimizedHiddenWidth
+ this.minimized = minimized
+
+ this.frame = document.createElement('div')
+ this.toolButtonMap = new Map()
+ Elements.setStyle(this.frame, {
+ width: this.width+"px",
+ height: this.height+"px",
+ backgroundColor: "#333",
+ position: "fixed",
+ visibility: "visible",
+ top: 0,
+ left: 0,
+ overflow: "auto"})
+
+
+ this.closeButton = this.addButton("../dev/tumortisch/icons/clear.png", "close")
+ this.minimizeButton = this.addButton("../dev/tumortisch/icons/remove.png", "minimize")
+
+ this.minimized == false ? this.minimizeButton.src = fileURL("../dev/tumortisch/icons/remove.png") : this.minimizeButton.src = fileURL("../dev/tumortisch/icons/fullscreen.png")
+ // this.addContentButton = this.addButton("../assets/icons/png/flat/note_add.png", "addContent")
+ // this.copyButton = this.addButton("../assets/icons/png/flat/content_copy.png", "copy")
+ // this.pasteButton = this.addButton("../assets/icons/png/flat/content_paste.png", "paste")
+ // this.saveButton = this.addButton("../assets/icons/png/flat/save.png", "save")
+ // this.deleteButton = this.addButton("../assets/icons/png/flat/delete.png", "delete")
+
+ document.body.appendChild( this.frame)
+ this.border = 50
+
+ this.frame.appendChild(this.element)
+
+ // this.pseudoFrame = document.createElement('div')
+ this.pseudoFrameTop = document.createElement('div')
+ this.pseudoFrameLeft = document.createElement('div')
+ this.pseudoFrameRight = document.createElement('div')
+
+ /*Elements.setStyle(this.pseudoFrame, {
+ position: "absolute",
+ top: "0px",
+ left: "0px",
+ width: "100%",
+ height: "100%",
+ pointerEvents: "none"
+ })*/
+
+ Elements.setStyle(this.pseudoFrameTop, {
+ position: "absolute",
+ // background: "pink",
+ top: "0px",
+ left: "0px",
+ width: "100%"
+ // height: "50px"
+ })
+
+ Elements.setStyle(this.pseudoFrameLeft, {
+ position: "absolute",
+ // background: "pink",
+ top: "0px",
+ left: "0px",
+ // width: "5px",
+ height: "100%"
+ })
+
+ Elements.setStyle(this.pseudoFrameRight, {
+ position: "absolute",
+ // background: "pink",
+ top: "0px",
+ right: "0px",
+ // width: "5px",
+ height: "100%"
+ })
+ this.frame.appendChild(this.pseudoFrameTop)
+ this.frame.appendChild(this.pseudoFrameLeft)
+ this.frame.appendChild(this.pseudoFrameRight)
+ // this.frame.appendChild(this.pseudoFrame)
+
+ this.pseudoFrameTop.addEventListener('pointerenter',(e)=>{
+ this.scatter.scalable=false
+ // this.element.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameTop.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameLeft.addEventListener('pointerenter',(e)=>{
+ // e.target.setPointerCapture(e.pointerId)
+ this.scatter.scalable=false
+ // this.element.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameLeft.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameRight.addEventListener('pointerenter',(e)=>{
+ this.scatter.scalable=false
+ // this.element.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameRight.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+ // this.element.style.overflow = "auto"
+ // this.element.style.position = "absolute"
+
+
+ /*this.closeButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.frame.style.visibility="hidden"
+ })*/
+
+ InteractionMapper.on('tap',this.minimizeButton, e => {
+ let s = this.scatter.scale
+ let x=0
+ let y=0
+ let deltaX = 0
+ let deltaY = 0
+ switch(this.minimized){
+ case false:
+
+ let w = this.frame.offsetWidth*s < 800 ? this.frame.offsetWidth : 800/s
+
+ x = this.frame.getBoundingClientRect().right - w
+ y = this.frame.getBoundingClientRect().bottom - this.border
+
+ deltaX = this.frame.offsetWidth - w
+ deltaY = this.frame.offsetHeight - this.border
+
+ this.minimizedHiddenHeight = this.frame.offsetHeight
+ this.minimizedHiddenWidth = this.frame.offsetWidth
+
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+
+ this.minimize(w, this.border, this.scatter.scale, deltaX*s, deltaY*s)
+ this.minimized = true
+ this.minimizeButton.src = fileURL("../dev/tumortisch/icons/fullscreen.png")
+ this.scatter.scalable = false
+ $(this.frame).children(".frameButton").each(function(i, obj) {
+ if(obj.value !="close" && obj.value !="closeAll" && obj.value !="minimize")obj.style.pointerEvents = "none"
+ })
+ // this.frame.style.pointerEvents = "none"
+ // this.minimizeButton.style.pointerEvents = "all"
+ // this.closeButton.style.pointerEvents = "all"
+ break
+ case true:
+
+ s = this.scatter.scale
+
+ x = this.frame.getBoundingClientRect().right - this.minimizedHiddenWidth
+ y = this.frame.getBoundingClientRect().bottom - this.minimizedHiddenHeight
+
+ deltaX = this.frame.getBoundingClientRect().width - this.minimizedHiddenWidth*s
+ deltaY = this.frame.getBoundingClientRect().height - this.minimizedHiddenHeight*s
+
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+
+ this.minimize(this.minimizedHiddenWidth, this.minimizedHiddenHeight, this.scatter.scale, deltaX, deltaY)
+ this.minimized = false
+ this.minimizeButton.src = fileURL("../dev/tumortisch/icons/remove.png")
+ this.scatter.scalable = true
+ $(this.frame).children(".frameButton").each(function(i, obj) {
+ obj.style.pointerEvents = "all"
+ })
+ // this.frame.style.pointerEvents = "all"
+ break
+ }
+ })
+
+ InteractionMapper.on('tap',this.closeButton, e => {
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.frame.style.visibility="hidden"
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "./icons/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+ // this.scatter.resizeButton.className = "frameButton"
+
+ this.scatter.bringToFront()
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ this.element.addEventListener('pointerdown', (e) => {
+ this.scatter.bringToFront()
+ })
+ this.layout()
+ }
+
+ minimize(width, height, scale, x, y) {
+
+ let oldPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+
+ this.frame.style.transformOrigin = "0% 0%"
+
+ let newPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+
+ let offset = Points.subtract(oldPostition, newPostition)
+
+ TweenLite.to(this.frame, 0, { css: { left: "+=" + offset.x + "px" } })
+ TweenLite.to(this.frame, 0, { css: { top: "+=" + offset.y + "px" } })
+
+ // TweenLite.to(this.frame, 0, { width: width , height: height, x: x, y: y })
+ TweenLite.to(this.frame, 0, { width: width , height: height})
+ TweenLite.to(this.frame, 0, { css: { left: "+=" + x + "px" } })
+ TweenLite.to(this.frame, 0, { css: { top: "+=" + y + "px" } })
+
+ oldPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+ this.frame.style.transformOrigin = "50% 50%"
+ newPostition = { x: $(this.frame).position().left, y: $(this.frame).position().top }
+ offset = Points.subtract(oldPostition, newPostition)
+
+ TweenLite.to(this.frame, 0, { css: { left: "+=" + offset.x + "px" } })
+ TweenLite.to(this.frame, 0, { css: { top: "+=" + offset.y + "px" } })
+
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ urlPadMap.delete(this.url)
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value=value
+ button.draggable = false
+ button.classList.add("interactiveElement")
+ this.frame.appendChild(button)
+ return button
+ }
+
+ addToolButton(src, value) {
+ console.log("adding tool button",value)
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value=value
+ button.draggable = false
+ this.frame.appendChild(button)
+ this.toolButtonMap.set(value,button)
+ return button
+ }
+
+ layout() {
+ console.log("layounting minmalPadFromElement")
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+
+ this.scatter.resizeButton.style.width=b+"px"
+ this.scatter.resizeButton.style.height=b+"px"
+
+ let size = "calc(100% - " + (2*b) +"px)"
+ let h = "calc(100% - " + (b) +"px)"
+ let w = "calc(100% - "+2 +"px)"
+ let w2 = "calc(100% - " + (b+2) +"px)"
+
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+
+ Elements.setStyle(this.element, {
+ width: w,
+ height: h,
+ top: 0+"px",
+ left: 0+"px"})
+
+ Elements.setStyle(this.pseudoFrameTop, {
+ width: w,
+ height: 0.1*b+"px",
+ margin: "0px"})
+ Elements.setStyle(this.pseudoFrameLeft, {
+ width: 0.1*b+"px",
+ height: h,
+ margin: "0px"})
+ Elements.setStyle(this.pseudoFrameRight, {
+ width: 0.1*b+"px",
+ height: h,
+ margin: "0px"})
+
+ Elements.setStyle(this.closeButton, {
+ right: (b * 0.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.minimizeButton, {
+ right: (b * 2.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ let buttonCounter=0
+ this.toolButtonMap.forEach(function (entry, key) {
+ // console.log(entry,key)
+ Elements.setStyle(entry, {
+ left: (b * (buttonCounter+0.75)) + "px",
+ bottom: "0px",
+ margin: 0.1*b+"px",
+ width: 0.8*b+"px",
+ height: 0.8*b+"px"})
+
+ buttonCounter++
+ }
+ )
+
+ /*Elements.setStyle(this.addContentButton, {
+ left: (b * 0.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.copyButton, {
+ left: (b * 1.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.pasteButton, {
+ left: (b * 2.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.saveButton, {
+ left: (b * 3.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.deleteButton, {
+ left: (b * 4.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})*/
+ }
+}
+
+module.exports = { Pad, minimalPad, DOMPadContainer, PadFromElement, minimalPadFromElement, PadAccordion }
diff --git a/browser/padAccordionOld.js b/browser/padAccordionOld.js
new file mode 100644
index 0000000..785fb27
--- /dev/null
+++ b/browser/padAccordionOld.js
@@ -0,0 +1,2814 @@
+const {fileURL} = require('./utils.js')
+const path = require('path')
+const electron = require('electron');
+
+/* A specialization that ignores webview events and thus allows
+ * webviews to get touch, mouse and wheel events.
+ */
+class DOMPadContainer extends DOMScatterContainer {
+
+ capture(event) {
+ if (event.target.tagName === 'WEBVIEW' || event.target.classList.contains('interactiveElement'))
+ return false
+ return super.capture(event)
+ }
+}
+
+/* A wrapper for a webview that behaves like a virtual tablet browser.
+ * Uses a DOMScatter to zoom and rotate the virtual browser window.
+ * The position of buttons and the border size remain constant.
+ */
+class Pad {
+
+ constructor(scatterContainer, {
+ startScale=1.0, minScale=0.25, maxScale=10.5,
+ autoBringToFront=true,
+ title='new Pad',
+ type='pad',
+ url="https://www.iwm-tuebingen.de/www/index.html",
+ hideOnStart=false,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ this.x = x
+ this.y = y
+ this.type = type
+ this.title=title
+ this.url = url
+ this.hideOnStart = hideOnStart
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.startScale = startScale
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.frame = document.createElement('div')
+ this.frame.classList.add("pad")
+ this.border = 50 / startScale
+
+ Elements.setStyle(this.frame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ display: 'flex',
+ width: this.width+"px",
+ height: this.height+"px",
+ top: 0,
+ left: 0,
+ visibility: "visible",
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ overflow: "visible"})
+
+ document.body.appendChild( this.frame)
+
+ this.web=document.createElement("webview")
+ this.webBackground=document.createElement("div")
+ this.webViewSnapshot=document.createElement("img")
+ this.overlay = document.createElement('div')
+
+ this.loadAnim = document.createElement("div")
+ this.loadAnim.style.webkitAnimation= "spin 2s linear infinite"
+ this.loadAnim.style.animation= "spin 2s linear infinite"
+ this.loadAnim.style.position = "absolute"
+
+ document.styleSheets[0].insertRule('\
+ @keyframes spin {\
+ from { transform: rotateZ(0deg); }\
+ to { transform: rotateZ(360deg); }\
+ }'
+ )
+
+ this.overlay.appendChild(this.loadAnim)
+
+ Elements.setStyle(this.web, {
+ position: "absolute",
+ overflow: "auto",
+ border: "1px solid #fff"
+ })
+
+ Elements.setStyle(this.webBackground, {
+ position: "absolute",
+ overflow: "auto",
+ background: "white"
+ })
+
+ Elements.setStyle(this.overlay, {
+ position: "absolute",
+ overflow: "auto",
+ background: "white",
+ opacity: "0.8"
+ })
+
+ Elements.setStyle(this.webViewSnapshot, {
+ position: "absolute",
+ overflow: "auto"
+ })
+
+ this.frame.appendChild(this.webBackground)
+ this.frame.appendChild(this.web)
+ this.frame.appendChild(this.webViewSnapshot)
+ // this.frame.appendChild(this.overlay)
+
+ this.webViewSnapshot.style.visibility="hidden"
+
+ this.web.src=url
+ this.web.preload= path.join(__dirname, './preloadPad.js')
+
+ this.closeButton = this.addButton("../assets/icons/svg/cross.svg", "close")
+ this.backButton = this.addButton("../assets/icons/svg/left.svg", "go back")
+ this.forwardButton = this.addButton("../assets/icons/svg/right.svg", "go forward")
+
+ this.backButton.style.opacity = 0.5
+ this.forwardButton.style.opacity = 0.5
+
+ /*for (let callback of window.padLoadedHandler) {
+ callback(this, url)
+ }*/
+ this.layout()
+
+ this.web.addEventListener('new-window', (e) => {
+
+ if(e.url.indexOf("youtube")>-1)return
+
+ if (urlPadMap.has(e.url)) {
+ let childPad = urlPadMap.get(e.url)
+ childPad.scatter.moveTo(x, y)
+ return childPad
+ }
+ let childPad = new Pad(this.scatterContainer, {
+ x: this.scatter.position.x+100,
+ y: this.scatter.position.y+100,
+ url: e.url,
+ width: this.scatter.width,
+ height: this.scatter.height,
+ scalable: true,
+ rotatable: true})
+ urlPadMap.set(e.url, childPad)
+
+ for(let callback of window.padLoadedHandler) {
+ callback(childPad, url)
+ }
+ })
+
+ this.web.addEventListener('did-navigate', (e) => {
+ this.enableButtons()
+ })
+
+ this.web.addEventListener('dom-ready',()=>{
+ //if(this.url.indexOf('local')>-1)this.web.openDevTools()
+ })
+
+ this.web.addEventListener('ipc-message', (e) => {
+ if(e.channel=='webviewPointerDown')this.scatter.bringToFront()
+ })
+
+ this.web.addEventListener('did-start-loading', ()=>{
+ this.overlay.style.visibility="visible"
+
+ let w = this.overlay.offsetWidth
+ let h = this.overlay.offsetHeight
+
+ // console.log("did start loading",h,w)
+
+ let animationSize = w{
+ this.overlay.style.visibility="hidden"
+ let query="#"+this.title+"expandButton"
+ $(query).css("opacity","1.0")
+ })
+
+ this.backButton.addEventListener('click', (e)=>{
+ if(this.web.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goBack()
+ }
+ })
+
+ this.forwardButton.addEventListener('click', (e)=>{
+ if(this.web.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goForward()
+ }
+ })
+
+ this.closeButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "../../assets/icons/png/flat/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+
+ this.scatter.moveTo({x, y})
+ this.scatter.bringToFront()
+
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ }
+
+ rad2degree(alpha){
+ return alpha * 180 / Math.PI;
+ }
+
+ degree2rad(alpha){
+ return alpha * Math.PI / 180;
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ urlPadMap.delete(this.url)
+ }
+
+ enableButtons() {
+ this.backButton.style.opacity = (this.web.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (this.web.canGoForward()) ? 1 : 0.5
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value="close"
+ button.draggable = false
+ this.frame.appendChild(button)
+ return button
+ }
+
+ layout() {
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+ let size = "calc(100% - " + (2*b+2) +"px)"
+ Elements.setStyle(this.web, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+ Elements.setStyle(this.webViewSnapshot, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+ Elements.setStyle(this.webBackground, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+
+ Elements.setStyle(this.overlay, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+ Elements.setStyle(this.closeButton, {
+ right: (b * 1.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.backButton, {
+ left: (b * 0.8) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.forwardButton, {
+ left: (this.border + (b * 0.8)) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ }
+
+}
+class PadAccordion {
+
+ constructor(scatterContainer, {
+ startScale=1.0, minScale=0.25, maxScale=10.5,
+ autoBringToFront=true,
+ type = 'accordion',
+ title="new Pad",
+ urlList=["https://www.iwm-tuebingen.de/www/index.html"],
+ hideOnStart=false,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ let w = window,
+ d = document,
+ e = d.documentElement,
+ g = d.getElementsByTagName('body')[0]
+ this.pageWidth = w.innerWidth || e.clientWidth || g.clientWidth,
+ this.pageHeight = w.innerHeight|| e.clientHeight|| g.clientHeight
+
+ this.x = x
+ this.y = y
+ this.type = type
+ this.title=title
+ this.urlList = urlList
+ this.hideOnStart = hideOnStart
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.startScale = startScale
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.webviewID = 0
+ this.externalPadID = 0
+ this.currentWebviewBackground = null
+ this.currentWebviewFrame = null
+ this.currentWebview = null
+ this.currentWebviewImage = null
+ this.currentSource=""
+ this.webviewMap = new Map()
+ this.padMap = new Map()
+ this.frame = document.createElement('div')
+ this.webFrame = document.createElement('div')
+ this.titlebar = document.createElement('div')
+ this.frame.classList.add("pad")
+ this.border = this.pageHeight*0.023148 / startScale
+
+ this.zIndex=1000
+ this.numWebviewsReady = 0
+
+ this.closeAllButton = this.addButton("../assets/icons/png/flat/clear_all.png", "close all")
+ this.closeButton = this.addButton("../assets/icons/png/flat/clear.png", "close")
+ this.backButton = this.addButton("../assets/icons/svg/left.svg", "go back")
+ this.forwardButton = this.addButton("../assets/icons/svg/right.svg", "go forward")
+ this.expandButton = this.addButton("../assets/icons/png/flat/launch.png", "expand")
+
+ this.expandButton.id=this.title+"expandButton"
+
+ this.backButton.style.opacity = 0.5
+ this.forwardButton.style.opacity = 0.5
+
+ /*let pad = new Pad(nodePadContainer, {
+ x: 500,
+ y: 200,
+ title: this.title,
+ url: "var/blank.html",
+ startScale:1,
+ hideOnStart: false,
+ width: 1500,
+ height: 1500,
+ scalable: true,
+ rotatable: false})
+
+
+ this.bufferPad=pad*/
+
+ // this.bufferPad.frame.id = this.title+"bufferPadFrame"
+ // this.bufferPad.web.id = this.title+"bufferPadWeb"
+ // this.bufferPad.frame.style.visibility="hidden"
+
+ Elements.setStyle(this.frame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ width: this.width+"px",
+ height: this.height+"px",
+ visibility: "visible",
+ top: 0,
+ left: 0
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ })
+
+ Elements.setStyle(this.webFrame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ // display: 'flex',
+ // flexFlow: "row no-wrap",
+ overflowX: "hidden",
+ width: "100%",
+ height: "80%",
+ top: 0,
+ left: 0
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ })
+
+ this.titlebar.innerHTML = this.title
+ Elements.setStyle(this.titlebar, {
+ color: "#fff",
+ // backgroundColor: "#F53",
+ // innerHTML:"title",
+ position: "absolute",
+ // display: 'flex',
+ // flexFlow: "row no-wrap",
+ overflowX: "hidden"
+ })
+
+ this.clicker=document.createElement('div')
+
+ Elements.setStyle(this.clicker, {
+ backgroundColor: "#F53",
+ position: "absolute",
+ width: "60px",
+ height: "50px",
+ bottom: 0,
+ left: 0
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ })
+
+
+ document.body.appendChild( this.frame)
+ this.frame.appendChild( this.webFrame)
+ this.frame.appendChild( this.titlebar)
+ // this.frame.appendChild( this.clicker)
+
+ this.clicker.addEventListener('click',(e)=>{
+ this.arrangeWebviews()
+ })
+
+ for(var i=0; i {
+
+ if(e.url.indexOf("youtube")>-1)return
+
+ if (urlPadMap.has(e.url)) {
+ let childPad = urlPadMap.get(e.url)
+ childPad.scatter.moveTo(x, y)
+ return childPad
+ }
+ let childPad = new Pad(this.scatterContainer, {
+ x: this.scatter.position.x+100,
+ y: this.scatter.position.y+100,
+ url: e.url,
+ width: this.scatter.width,
+ height: this.scatter.height,
+ scalable: true,
+ rotatable: true})
+ urlPadMap.set(e.url, childPad)
+
+ for(let callback of window.padLoadedHandler) {
+ callback(childPad, url)
+ }
+ })
+
+ this.web.addEventListener('did-navigate', (e) => {
+ this.enableButtons()
+ })
+
+ this.web.addEventListener('dom-ready',()=>{
+ //if(this.url.indexOf('local')>-1)this.web.openDevTools()
+ })
+
+ this.web.addEventListener('ipc-message', (e) => {
+ if(e.channel=='webviewPointerDown')this.scatter.bringToFront()
+ })
+
+ this.web.addEventListener('did-start-loading', ()=>{
+ this.overlay.style.visibility="visible"
+
+ let w = this.overlay.offsetWidth
+ let h = this.overlay.offsetHeight
+
+ console.log("did start loading",h,w)
+
+ let animationSize = w{
+ this.overlay.style.visibility="hidden"
+ })*/
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "../../assets/icons/png/flat/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+
+ this.scatter.moveTo({x, y})
+ this.scatter.bringToFront()
+
+ this.currentWebviewHeight = 0
+
+ this.scatter.element.addEventListener('resized',(e)=>{
+ // $(this.webFrame).find('.accordionWebimage').css('height',this.currentWebviewHeight)
+
+ this.arrangeWebviews()
+ // console.log("resized",e,e.detail)
+ })
+
+ this.scatter.element.addEventListener('resizeStarted',()=>{
+ // this.currentWebviewHeight = this.webFrame.offsetHeight
+ // console.log("resizeStarted")
+ })
+
+ this.scatter.element.addEventListener('resizeEnded',()=>{
+ /*let h = this.webFrame.offsetHeight
+ let cWebview = this.currentWebview
+ $(this.webFrame).find('.accordionWebview').each(function(i, obj) {
+ // obj.style.visibility="visible"
+ if(obj!=cWebview){
+ obj.style.visibility="visible"
+ obj.capturePage((image)=>{
+ obj.parentElement.childNodes[3].src=image.toDataURL()
+
+ obj.style.visibility="hidden"
+ $(this.webFrame).find('.accordionWebimage').css('height',h)
+ })
+
+ }
+ })*/
+ // console.log("resizeEnded")
+ })
+
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = this.pageHeight*0.023148 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ else{
+ }
+ })
+ this.layout()
+
+ this.backButton.addEventListener('click', (e)=>{
+ if(this.currentWebview.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.currentWebview.goBack()
+ }
+ })
+
+ this.forwardButton.addEventListener('click', (e)=>{
+ if(this.currentWebview.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.currentWebview.goForward()
+ }
+ })
+
+ this.closeAllButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })
+
+ this.closeButton.addEventListener('click', (e)=>{
+ if(this.webviewMap.size>1)this.removeWebView()
+ })
+
+ this.expandButton.addEventListener('click',(e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.externalPadID++
+ let newPadKey=this.title+this.externalPadID
+ console.log("NEW PAD KEY",newPadKey)
+ // console.log("expanding",this.currentWebview.getTitle())
+ // console.log("expanding",this.currentWebview.getURL())
+
+ let newPad= new minimalPad(nodePadContainer, {
+ x: 500,
+ y: 200,
+ padMapKey: newPadKey,
+ title: this.title+" - "+ this.expandButton.getAttribute('source'),//this.currentWebview.getTitle(),
+ urlList: [this.currentWebview.src],
+ startScale:1,
+ hideOnStart: false,
+ width: this.pageWidth*0.3906,
+ height: this.pageWidth*0.3906,
+ scalable: true,
+ rotatable: false})
+
+ urlPadMap.set(newPadKey,newPad)
+ /* document.getElementById(this.title+"bufferPadFrame").style.visibility="visible"
+ document.getElementById(this.title+"bufferPadFrame").id=""
+ document.getElementById(this.title+"bufferPadWeb").id=""*/
+ /*if(e.target.style.opacity=="1"){
+ this.bufferPad.frame.style.visibility="visible"
+ this.bufferPad.scatter.bringToFront()
+ // this.bufferPad.frame.id=""
+ // this.bufferPad.web.id=""
+
+ let newPad= new Pad(nodePadContainer, {
+ x: 500,
+ y: 200,
+ title: this.title,
+ url: "var/blank.html",
+ startScale:1,
+ hideOnStart: false,
+ width: 1500,
+ height: 1500,
+ scalable: true,
+ rotatable: false})
+
+ this.bufferPad=newPad
+ // newPad.web.src=this.currentWebview.src
+ // this.bufferPad.frame.id = this.title+"bufferPadFrame"
+ // this.bufferPad.web.id = this.title+"bufferPadWeb"
+ this.bufferPad.frame.style.visibility = "hidden"
+ this.bufferPad.web.src=this.currentWebview.src
+ }*/
+ })
+ // TweenMax.to(this.frame,0,{opacity:"0.001"})
+
+ }
+
+ removeWebView(){
+ let webVIewRemovedID=parseInt(this.currentWebview.getAttribute('id'))
+
+ this.webviewMap.delete(webVIewRemovedID)
+ this.webFrame.removeChild(this.currentWebview.parentNode)
+
+ let cWeb = null
+ let cWebviewFrame = null
+
+ this.webviewMap.forEach(function (entry, key) {
+ cWeb = entry.childNodes[3]
+ cWebviewFrame = entry
+ })
+ this.currentWebview = cWeb
+ this.currentWebviewFrame = cWebviewFrame
+ this.currentSource = cWeb.src
+ this.bringWebToFront()
+ }
+
+ appendWebView(newUrl,source){
+ let fs = nodeRequire("fs")
+
+ let web=document.createElement("webview")
+ let webOffloading=document.createElement("webview")
+ let webImage=document.createElement("img")
+ let heinz=document.createElement("img")
+ let webBackground=document.createElement("div")
+ let webviewFrame=document.createElement("div")
+ let shadow=document.createElement("img")
+ let shadowReverse=document.createElement("img")
+ let overlay = document.createElement('img')
+ let progressBar = document.createElement('div')
+
+ web.setAttribute("plugins",true)
+ web.classList.add("accordionWebview")
+ // web.classList.add("interactiveElement")
+ webviewFrame.classList.add("accordionWebviewFrame")
+ shadow.classList.add("accordionWebviewShadow")
+ shadowReverse.classList.add("accordionWebviewShadow")
+ webBackground.classList.add("accordionWebviewBackground")
+ webBackground.setAttribute('source',source)
+ webImage.classList.add("accordionWebimage")
+ overlay.classList.add("accordionOverlay")
+ overlay.src="img/overlay.png"
+
+ webviewFrame.classList.add("interactiveElement")
+ webBackground.classList.add("interactiveElement")
+ webImage.classList.add("interactiveElement")
+ overlay.classList.add("interactiveElement")
+ web.classList.add("interactiveElement")
+
+ let loadAnim = document.createElement("div")
+ loadAnim.style.webkitAnimation= "spin 2s linear infinite"
+ loadAnim.style.animation= "spin 2s linear infinite"
+ loadAnim.style.position = "absolute"
+
+ document.styleSheets[0].insertRule('\
+ @keyframes spin {\
+ from { transform: rotateZ(0deg); }\
+ to { transform: rotateZ(360deg); }\
+ }'
+ )
+
+ // overlay.appendChild(loadAnim)
+
+ // webBackground.className= "item"
+
+ Elements.setStyle(webviewFrame, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: this.webviewID*50+"px",
+ // display:"flex",
+ width: "20%",
+ left: "0%",
+ // minWidth:"5px",
+ // maxWidth:"500px",
+ height: "100%"
+ // pointerEvents: 'none',
+ // boxShadow: "-5px 5px 50px 10px #555",
+ // border:"2px solid blue",
+ // overflow: "hidden",
+ // overflowX: "scroll",
+ // flex: "0 0 50%",
+ // whiteSpace: "nowrap",
+ })
+
+ Elements.setStyle(webBackground, {
+ position: "absolute",
+ overflow: "hidden",
+ left: "5%",
+ // display:"flex",
+ width: "90%",
+ // pointerEvents: 'none',
+ // minWidth:"5px",
+ // maxWidth:"500px",
+ height: "100%",
+ // boxShadow: "-5px 5px 50px 10px #555",
+ // border:"2px solid blue",
+ // overflow: "hidden",
+ // overflowX: "scroll",
+ // flex: "0 0 50%",
+ // whiteSpace: "nowrap",
+ background: "white"
+ })
+
+ shadow.src = "img/shadow.png"
+ Elements.setStyle(shadow, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "95%",
+ width: "5%",
+ height: "100%",
+ // background: "#252",
+ // opacity: "0.1",
+ pointerEvents:"none"
+ })
+
+ shadowReverse.src = "img/shadowReverse.png"
+ Elements.setStyle(shadowReverse, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "0%",
+ width: "5%",
+ height: "100%",
+ // background: "#252",
+ // opacity: "0.1",
+ pointerEvents:"none"
+ })
+
+ Elements.setStyle(web, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "5%",
+ width: "90%",
+ // pointerEvents: 'none',
+ // filter: "blur(10px)",
+ // display: "none",
+ visibility: "hidden",
+ // border: "1px solid #fff",
+ height: "100%"
+ })
+
+ Elements.setStyle(webOffloading, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "-100%",
+ width: "90%",
+ // pointerEvents: 'none',
+ // filter: "blur(10px)",
+ // display: "none",
+ visibility: "visible",
+ // border: "1px solid #fff",
+ height: "100%"
+ })
+
+ Elements.setStyle(webImage, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "5%",
+ width: "90%",
+ height: this.webFrame.offsetHeight+"px",
+ pointerEvents: 'none',
+ // display: "none",
+ visibility: "visible"
+ // border: "1px solid #fff",
+ })
+
+ Elements.setStyle(heinz, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "0px",
+ width: "100%",
+ background: "pink",
+ // display: "none",
+ visibility: "visible",
+ // border: "1px solid #fff",
+ height: "100%"
+ })
+
+ Elements.setStyle(overlay, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0px",
+ left: "5%",
+ // background: "pink",
+ // pointerEvents: 'none',
+ width: "90%",
+ height: "100%"
+ // opacity: "0.1"
+ })
+
+ Elements.setStyle(progressBar, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0%",
+ left: "10%",
+ visibility: "hidden",
+ background: "white",
+ filter: "blur(50px)",
+ opacity: "0.2",
+ // pointerEvents: 'none',
+ width: "40%",
+ height: "100%"
+ // opacity: "0.1"
+ })
+
+
+ this.currentWebviewFrame=webviewFrame
+ this.currentWebviewBackground=webBackground
+ this.currentWebview = web
+ this.currentWebviewImage = webImage
+ this.currentSource = source
+
+ // document.body.appendChild(webOffloading)
+
+ this.webFrame.appendChild(webviewFrame)
+ webviewFrame.appendChild(webBackground)
+ webviewFrame.appendChild(shadow)
+ webviewFrame.appendChild(shadowReverse)
+ webviewFrame.appendChild(web)
+ webviewFrame.appendChild(webImage)
+ // webBackground.appendChild(heinz)
+ webviewFrame.appendChild(overlay)
+ webviewFrame.appendChild(progressBar)
+
+ web.setAttribute('id',this.webviewID)
+ web.setAttribute('isLoading','true')
+ this.webviewMap.set(this.webviewID, webviewFrame)
+ this.webviewID++
+ web.src=newUrl
+ webOffloading.src=newUrl
+
+ // web.src="https://www.golem.de/"
+
+ // console.log("Appending WebView",newUrl)
+ // // document.getElementById(this.title+"bufferPadWeb").src=newUrl
+ // webImage.src='var/'+source+'.png'
+ web.preload= path.join(__dirname, './preloadPad.js')
+
+ web.addEventListener('ipc-message', (e) => {
+ if(e.channel=='webviewPointerDown')this.scatter.bringToFront()
+ })
+
+ web.addEventListener('did-fail-load',()=>{
+ console.log("failed loading",newUrl)
+ })
+ let obj = { blur: 2 }
+ let loadAnimation = new TimelineMax({repeat:-1})
+ // loadAnimation.add( TweenMax.to(obj, 0.5, {blur: 5,onUpdate: () => {web.style.filter = "blur(" + obj.blur + "px)"}}) )
+ // loadAnimation.add( TweenMax.to(obj, 0.5, {blur: 2,onUpdate: () => {web.style.filter = "blur(" + obj.blur + "px)"}}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0.5, {left:"50%"}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0, {left:"20%"}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0.5, {left:"50%"}) )
+ loadAnimation.stop()
+ // loadAnimation.add( TweenLite.to(web, 1, {filter:"blur(0px)"}) );
+
+ web.addEventListener('did-start-loading', ()=>{
+ web.setAttribute('isLoading','true')
+ loadAnimation.play()
+ overlay.style.visibility="visible"
+ progressBar.style.visibility="visible"
+ web.style.filter = "blur(10px)"
+ overlay.style.pointerEvents="none"
+
+ // web.style.filter = "blur(10px)"
+
+ // console.log('did-start-loading')
+ // loadAnim.style.visibility="visible"
+
+ let w = overlay.offsetWidth
+ let h = overlay.offsetHeight
+
+ // console.log("accordion webview did start loading",h,w)
+
+ /*let animationSize = w{
+
+ /*let win = new BrowserWindow({width: 800, height: 600, x: -1000, y: -1000, webPreferences: {offscreen: true}})
+ win.webContents.on('paint', (event, dirty, image) => {
+ webImage.src=image.toDataURL()
+ win.close()
+ })
+ win.loadURL(newUrl)*/
+
+ if(this.currentWebview==web)overlay.style.visibility="hidden"
+ overlay.style.pointerEvents="all"
+ // loadAnimation.stop()
+ loadAnimation.stop()
+ progressBar.style.visibility="hidden"
+ web.style.filter = "blur(0px)"
+ web.setAttribute('isLoading','false')
+ // console.log('did-stop-loading')
+ // loadAnim.style.visibility="hidden"
+ if(web==this.currentWebview){
+ // this.bufferPad.web.src=web.src
+ }
+ if(web!=this.currentWebview){
+ }
+ // if(this.webviewID{
+ // webImage.src=image.toDataURL()
+ })*/
+
+ // console.log('did-stop-loading',webImage)
+ })
+ /*web.addEventListener('did-navigate', ()=>{
+ console.log('did-navigate')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })
+ web.addEventListener('did-navigate-in-page', ()=>{
+ console.log('did-navigate-in-page')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })
+ web.addEventListener('did-frame-finish-load', ()=>{
+ console.log('did-frame-finish-load')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })
+ web.addEventListener('load-commit', ()=>{
+ console.log('load-commit')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })
+ web.addEventListener('page-title-updated', ()=>{
+ console.log('page-title-updated')
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+ })
+ })*/
+
+
+ /*let phantom = require('phantom')
+
+ let date = new Date()
+
+ let captureFile=nodePath.join(nodeDirname, '../dev/tumortisch/var/PhantomScreenshots/phantom_'+source+date.getMinutes()+date.getSeconds()+'.png')
+ if (fs.existsSync(captureFile)) {
+ fs.unlink(captureFile, (err) => {
+ if (err) {
+ return
+ }
+ console.log("File succesfully deleted")
+ })
+ }
+ phantom.create().then(function(ph) {
+ ph.createPage().then(function(page) {
+ page.open(web.src).then(function(status) {
+ page.render(captureFile).then(function(){
+ let tmpIMG=document.createElement('img')
+ tmpIMG.src=captureFile
+ tmpIMG.addEventListener('load',()=>{
+ webImage.src=captureFile
+ webImage.style.height = tmpIMG.height+"px"
+ // webImage.style.width = tmpIMG.width+"px"
+ })
+ })
+ })
+ })
+ })*/
+ web.addEventListener('dom-ready', ()=>{
+
+ /*let id=this.webviewID
+ let nextURL = this.urlList[id].url
+ let nextSource = this.urlList[id].source*/
+
+ // webImage.src='var/'+source+'.png'
+ // console.log('dom-ready')
+ web.setAttribute('isLoading','false')
+ if(web==this.currentWebview){
+ // this.bufferPad.web.src=web.src
+ }
+ if(web!=this.currentWebview){
+ }
+ /* this.currentWebviewBackground = web.parentElement
+ this.currentWebview = web
+ this.currentSource = source
+ this.arrangeWebviews()*/
+
+ let captureRect = {
+ x: 0, y: 0,
+ width: 2000,
+ height: 4000
+ }
+ web.capturePage(captureRect,function(image) {
+ webImage.src=image.toDataURL()
+ console.log("web.capturePage",newUrl)
+ let buf = image.toPNG()
+ let captureFile=nodePath.join(nodeDirname, '../dev/tumortisch/var/'+source+'.png')
+
+ fs.writeFile(captureFile, buf, function(err) {
+ if (err) {
+ console.log("ERROR Failed to save file", err)
+ }
+ else{
+ }
+ })
+ })
+ /*this.numWebviewsReady++
+ if(this.numWebviewsReady==7){
+ this.arrangeWebviews()
+ TweenMax.to(this.frame,0.0,{opacity:"1"})
+ }*/
+
+ /* web.capturePage((image)=>{
+ // webImage.src='var/cosmic.png'
+ // console.log("Captured after Load",web.src)
+ webImage.src=image.toDataURL()
+
+ /* let captureFile=nodePath.join(nodeDirname, '../dev/tumortisch/var/'+source+'.png')
+ let buf = image.toPng()
+
+ fs.writeFile(captureFile, buf, function(err) {
+ if (err) {
+ console.log("ERROR Failed to save file", err)
+ }
+ else{
+ }
+ })*/
+ //})
+ this.backButton.style.opacity = (web.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (web.canGoForward()) ? 1 : 0.5
+ // webImage.style.display="inline"
+ // web.openDevTools()
+ })
+
+ web.addEventListener('new-window', (e) => {
+ this.appendWebView(e.url)
+ console.log("this.currentSource",source)
+ this.urlList.push({'url':e.url,'source':source})
+ })
+
+ overlay.addEventListener('click',(e)=>{
+ // console.log("Overlay wurde geklickt",this.currentWebview.src)
+ e.stopPropagation()
+ e.preventDefault()
+ this.currentWebview.capturePage((image)=>{
+ console.log(this.webFrame.offsetHeight)
+ this.currentWebviewImage.style.height = this.webFrame.offsetHeight+"px"//"100%"
+ this.currentWebviewImage.src=image.toDataURL()
+ this.currentWebviewFrame = e.target.parentElement
+ // this.bringWebToFront()
+ e.target.style.visibility="hidden"
+
+ this.currentSource=source
+ this.expandButton.setAttribute('source',source)
+
+ this.currentWebviewFrame = e.target.parentElement
+ this.currentWebview = web
+ this.currentSource = source
+ this.currentWebviewImage = webImage
+ this.arrangeWebviews()
+ // this.bufferPad.web.src=web.src
+
+ this.backButton.style.opacity = (web.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (web.canGoForward()) ? 1 : 0.5
+ })
+ //e.target.parentElement.style.zIndex=this.zIndex
+
+ })
+ /*overlay.addEventListener('click',(e)=>{
+ e.stopPropagation()
+ e.preventDefault()
+ this.currentWebviewBackground = e.target.parentElement
+ this.bringWebToFront()
+ e.target.style.visibility="hidden"
+ //e.target.parentElement.style.zIndex=this.zIndex
+ })*/
+
+ webImage.addEventListener('pointerenter',(e)=>{
+ // TweenMax.to(webBackground, 0.1, {x:"+= 50"})
+ })
+
+ webImage.addEventListener('pointerleave',(e)=>{
+ // TweenMax.to(webBackground, 0.1, {x:"-= 50"})
+ })
+
+ webImage.addEventListener('click',(e)=>{
+
+ this.currentSource=source
+ this.expandButton.setAttribute('source',source)
+
+ // this.expandButton.style.opacity="0.5"
+ e.stopPropagation()
+ e.preventDefault()
+ let oldWeb=this.currentWebview
+ let oldWebImage = this.currentWebviewImage
+
+ oldWeb.capturePage((image)=>{
+ oldWebImage.src=image.toDataURL()
+
+ this.currentWebviewBackground = e.target.parentElement
+ this.currentWebview = web
+ this.currentWebviewImage = webImage
+ this.currentSource = source
+ this.arrangeWebviews()
+ })
+
+ web.capturePage((image)=>{
+ webImage.src=image.toDataURL()
+
+ /* let captureFile=nodePath.join(nodeDirname, '../dev/tumortisch/var/'+source+'.png')
+ let buf = image.toPNG()
+
+ fs.writeFile(captureFile, buf, function(err) {
+ if (err) {
+ console.log("ERROR Failed to save file", err)
+ }
+ else{
+ }
+ })*/
+ })
+
+ // this.bufferPad.web.src=web.src
+
+ this.backButton.style.opacity = (web.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (web.canGoForward()) ? 1 : 0.5
+ // e.target.style.visibility="hidden"
+ //e.target.parentElement.style.zIndex=this.zIndex
+ })
+
+ /*overlay.addEventListener('pointerenter',(e)=>{
+ TweenMax.to(webBackground, 0.1, {x:"+= 20"})
+ })
+
+ overlay.addEventListener('pointerleave',(e)=>{
+ TweenMax.to(webBackground, 0.1, {x:"-= 20"})
+ })*/
+ this.arrangeWebviews()
+ }
+
+ arrangeWebviews(){
+ let numWebviews = this.webviewMap.size
+ let index = 0
+ let increment = 1
+ let z = this.zIndex
+ let current=this.currentWebviewFrame
+ let shadowWidth = 0
+ this.webviewMap.forEach(function (entry, key) {
+ entry.style.maxWidth="1000px"
+ // entry.style.width=100-Math.sqrt(entry.parentElement.offsetWidth-590)+"%"
+ entry.style.width="70%"
+ shadowWidth = $(entry).find(".accordionWebviewShadow").outerWidth()
+ // console.log("entry.parentElement.offsetWidth",entry.parentElement.offsetWidth)
+ z+=increment
+ let offset=index*(entry.parentElement.offsetWidth+2*shadowWidth-entry.offsetWidth)/(numWebviews-1)
+ // console.log("Offset",entry.parentElement.offsetWidth-(offset+entry.offsetWidth))
+ entry.style.left=entry.parentElement.offsetWidth+2*shadowWidth-(offset+entry.offsetWidth)-shadowWidth+"px"
+ entry.style.zIndex=z
+ // $( entry ).children(".accordionWebview[isLoading='false']").css( "visibility", "hidden" )
+ $( entry ).children(".accordionWebview").css( "visibility", "hidden" )
+ /* $( entry ).children(".accordionWebview").css( "visibility", "hidden" ).each(function(i, obj) {
+ console.log("isLoading()",obj.isLoading())
+ })*/
+ $( entry ).children('.accordionOverlay').css( "visibility", "visible" )
+ $( entry ).children('.accordionWebimage').css( "visibility", "visible" )
+ // entry.style.left=index*(entry.parentElement.offsetWidth-entry.offsetWidth)/(numWebviews-1)+"px"
+ index++
+ if(entry==current){
+ $( entry ).children('.accordionWebview').css( "visibility", "visible" )
+ $( entry ).children('.accordionOverlay').css( "visibility", "hidden" )
+ $( entry ).children('.accordionWebimage').css( "visibility", "hidden" )
+ increment*=-1
+ // entry.childNodes[1].style.opacity=0.8
+ }
+ })
+ this.zIndex=z
+
+ // this.enableButtons()
+ /*this.zIndex++
+ this.currentWebviewBackground.style.zIndex=this.zIndex
+ $( this.currentWebviewBackground ).children('.accordionWebview').css( "visibility", "visible" )
+ $( this.currentWebviewBackground ).children('.accordionOverlay').css( "visibility", "hidden" )
+ $( this.currentWebviewBackground ).children('.accordionWebimage').css( "visibility", "hidden" )*/
+}
+
+ bringWebToFront(){
+ this.arrangeWebviews()
+ this.zIndex++
+ this.currentWebviewFrame.style.zIndex=this.zIndex
+ // this.currentWebviewBackground.childNodes[2].style.opacity=0.8
+ }
+ rad2degree(alpha){
+ return alpha * 180 / Math.PI;
+ }
+
+ degree2rad(alpha){
+ return alpha * Math.PI / 180;
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ urlPadMap.delete(this.url)
+ urlPadMap.delete(this.title)
+ let geneTab = document.getElementById(this.title +"_tab")
+ TweenMax.to(geneTab, 0.4, {scaleX:"1.0",x:"0px"});
+ geneTab.style.boxShadow = "none"
+ geneTab.setAttribute('open','false')
+ }
+
+ enableButtons() {
+ this.backButton.style.opacity = (this.currentWebview.canGoBack()) ? 1 : 0.5
+ this.forwardButton.style.opacity = (this.currentWebview.canGoForward()) ? 1 : 0.5
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value="close"
+ button.draggable = false
+ this.frame.appendChild(button)
+ return button
+ }
+
+ layout() {
+ console.log("LAYOUT")
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+ let size = "calc(100% - " + (2*b+2) +"px)"
+ let h = "calc(100% - " + (b) +"px)"
+ let w = "calc(100% - "+2 +"px)"
+
+ this.scatter.resizeButton.style.width=b+"px"
+ this.scatter.resizeButton.style.height=b+"px"
+
+ Elements.setStyle(this.webFrame, {
+ width: w,
+ height: h,
+ margin: "1px"})
+ /*Elements.setStyle(this.webBackground, {
+ width: size,
+ height: size,
+ margin: b+"px"})
+
+ Elements.setStyle(this.overlay, {
+ width: size,
+ height: size,
+ margin: b+"px"})*/
+
+ Elements.setStyle(this.titlebar, {
+ left: (b * 5.75) + "px",
+ bottom: "0px",
+ fontSize: 0.7*b+"px",
+ // paddingTop: 10+"px",
+ // textAlign: "center",
+ margin: 0.1*b+"px",
+ height: 0.8*b+"px"})
+ Elements.setStyle(this.closeAllButton, {
+ right: (b * 1.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.backButton, {
+ left: (b * 0.8) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.forwardButton, {
+ left: (this.border + (b * 0.8)) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.expandButton, {
+ left: (this.border + (b * 1.8)) +"px",
+ bottom: "0px",
+ // opacity: "0.5",
+ margin: 0.1*b+"px",
+ width: 0.8*b+"px",
+ height: 0.8*b+"px"})
+ Elements.setStyle(this.closeButton, {
+ left: (this.border + (b * 2.8)) +"px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ }
+
+}
+
+class PadFromElement {
+
+ constructor(element, scatterContainer, {
+ startScale=1.0, minScale=0.1, maxScale=1.0,
+ autoBringToFront=true,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ type = 'fromElement',
+ title = 'New Pad',
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ this.element = element
+ this.type = type
+ this.title = title
+ this.x = x
+ this.y = y
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.frame = document.createElement('div')
+ Elements.setStyle(this.frame, {
+ width: this.width+"px",
+ height: this.height+"px",
+ backgroundColor: "#333",
+ position: "fixed",
+ visibility: "visible",
+ top: 0,
+ left: 0,
+ overflow: "auto"})
+
+ this.closeButton = this.addButton("../assets/icons/svg/cross.svg", "close")
+
+ document.body.appendChild( this.frame)
+ this.border = 50
+
+ this.frame.appendChild(this.element)
+
+ this.title = document.createElement("div")
+ this.title.innerHTML = "Titel"
+ this.title.style.color = "white"
+ this.frame.appendChild(this.title)
+
+ Elements.setStyle(this.title, {
+ position: "absolute"
+ })
+ // this.element.style.overflow = "auto"
+ // this.element.style.position = "absolute"
+
+ this.layout()
+
+ this.closeButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.frame.style.visibility="hidden"
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ this.scatter.bringToFront()
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ this.element.addEventListener('pointerdown', (e) => {
+ this.scatter.bringToFront()
+ })
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ // urlPadMap.delete(this.url)
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value="close"
+ button.draggable = false
+ this.frame.appendChild(button)
+ return button
+ }
+
+ layout() {
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+ let size = "calc(100% - " + (2*b+2) +"px)"
+ Elements.setStyle(this.element, {
+ width: size,
+ height: size,
+ top: b+"px",
+ left: b+"px"})
+ Elements.setStyle(this.closeButton, {
+ right: (b * 0.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ Elements.setStyle(this.title, {
+ left: (b * 1.5) + "px",
+ fontSize: (b * 0.8) + "px",
+ top: (0.1)+"0px"})
+ }
+}
+class minimalPad {
+
+ constructor(scatterContainer, {
+ startScale=1.0, minScale=0.25, maxScale=10.5,
+ autoBringToFront=true,
+ type = 'minimal',
+ title="new Pad",
+ urlList=["https://www.iwm-tuebingen.de/www/index.html"],
+ padMapKey="key",
+ // url="https://www.iwm-tuebingen.de/www/index.html",
+ hideOnStart=false,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ let w = window,
+ d = document,
+ e = d.documentElement,
+ g = d.getElementsByTagName('body')[0]
+ this.pageWidth = w.innerWidth || e.clientWidth || g.clientWidth,
+ this.pageHeight = w.innerHeight|| e.clientHeight|| g.clientHeight
+
+ this.x = x
+ this.y = y
+ this.type = type
+ // this.url = url
+ this.urlList = urlList
+ this.padMapKey = padMapKey,
+ this.title = title,
+ this.hideOnStart = hideOnStart
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.startScale = startScale
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.web = null
+ this.WebviewCounter = 0
+ this.id=0
+
+ this.webviewMap = new Map()
+ this.overlayMap = new Map()
+ this.tabMap = new Map()
+ this.listMap = new Map()
+
+ this.frame = document.createElement('div')
+ this.frame.classList.add("pad")
+ this.border = this.pageHeight*0.023148 / startScale
+
+ Elements.setStyle(this.frame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ display: 'flex',
+ width: this.width+"px",
+ height: this.height+"px",
+ visibility: "visible",
+ top: 0,
+ left: 0,
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ overflow: "visible"})
+
+ document.body.appendChild( this.frame)
+
+ this.tabs=document.createElement("div")
+ this.webBackground=document.createElement("div")
+ this.webviewList=document.createElement("div")
+ this.titlebar = document.createElement("div")
+ this.webViewSnapshot=document.createElement("img")
+
+ this.tabs.classList.add("interactiveElement")
+ this.tabs.classList.add("tabs")
+ // this.tabs.style.display="flex"
+ // this.tabs.style.flexFlow="row nowrap"
+
+ Elements.setStyle(this.tabs, {
+ position: "absolute",
+ background: "white",
+ overflowX: "auto",
+ overflowY: "hidden",
+ display: "flex",
+ flexFlow:"row nowrap",
+ // justifyContent: "flex-end",
+ alignItems: "flex-end"
+ // overflow: "auto",
+ })
+
+ this.titlebar.innerHTML = this.title
+ Elements.setStyle(this.titlebar, {
+ color: "#fff",
+ position: "absolute",
+ overflowX: "hidden"
+ })
+
+ Elements.setStyle(this.webBackground, {
+ position: "absolute",
+ overflow: "hidden",
+ background: "white"
+ })
+
+ this.webviewList.classList.add("interactiveElement")
+
+ Elements.setStyle(this.webviewList, {
+ position: "absolute",
+ visibility: "hidden",
+ overflow: "auto",
+ background: "white",
+ boxShadow: "5px 5px 10px #bbb"
+ })
+
+ /*Elements.setStyle(this.overlay, {
+ position: "absolute",
+ overflow: "auto",
+ background: "white",
+ opacity: "0.8"
+ })*/
+
+ Elements.setStyle(this.webViewSnapshot, {
+ position: "absolute",
+ overflow: "auto"
+ })
+
+ this.frame.appendChild(this.titlebar)
+ this.frame.appendChild(this.webBackground)
+ // this.frame.appendChild(this.webViewSnapshot)
+ this.frame.appendChild(this.tabs)
+ this.frame.appendChild(this.webviewList)
+
+ for(var i=0; i{
+ this.scatter.scalable=false
+ this.web.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameTop.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameLeft.addEventListener('pointerenter',(e)=>{
+ // e.target.setPointerCapture(e.pointerId)
+ this.scatter.scalable=false
+ this.web.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameLeft.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameRight.addEventListener('pointerenter',(e)=>{
+ this.scatter.scalable=false
+ this.web.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameRight.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ /*this.web.addEventListener('new-window', (e) => {
+
+ if(e.url.indexOf("youtube")>-1)return
+
+ if (urlPadMap.has(e.url)) {
+ let childPad = urlPadMap.get(e.url)
+ childPad.scatter.moveTo(x, y)
+ return childPad
+ }
+ let childPad = new minimalPad(this.scatterContainer, {
+ x: this.scatter.position.x+100,
+ y: this.scatter.position.y+100,
+ url: e.url,
+ width: this.scatter.width,
+ height: this.scatter.height,
+ scalable: true,
+ rotatable: true})
+ urlPadMap.set(e.url, childPad)
+
+ for(let callback of window.padLoadedHandler) {
+ callback(childPad, url)
+ }
+ })*/
+
+ this.backButton.addEventListener('click', (e)=>{
+ if(this.web.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goBack()
+ }
+ })
+
+ this.forwardButton.addEventListener('click', (e)=>{
+ if(this.web.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goForward()
+ }
+ })
+
+ this.closeButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })
+
+ this.showWebviewListButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.webviewList.style.visibility=="visible" ? this.webviewList.style.visibility="hidden" : this.webviewList.style.visibility="visible"
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "../../assets/icons/png/flat/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+
+ this.scatter.moveTo({x, y})
+ this.scatter.bringToFront()
+
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = this.pageHeight*0.023148 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ this.layout()
+
+ /*this.test = document.createElement('div')
+
+ Elements.setStyle(this.test, {
+ position: "absolute",
+ width: "400px",
+ height: "400px",
+ overflow: "auto",
+ background: "pink"
+ })
+ this.frame.appendChild(this.test)
+
+ this.test.addEventListener('click',()=>{
+ // this.frame.style.visibility="hidden"
+ console.log($( this.webBackground ).children())
+ $( this.webBackground ).children().css( "visibility", "hidden" )
+ $( this.frame ).children().css( "visibility", "hidden" )
+ })*/
+
+ }
+
+ addNewWebview(src){
+ /* if(this.web!=null){
+ this.web.style.visibility="hidden"
+ this.web.setAttribute('selected','0')
+ }*/
+
+ let webview=document.createElement("webview")
+ let progressBar = document.createElement('div')
+ webview.setAttribute("plugins",true)
+ this.web=webview
+ webview.setAttribute('loaded','0')
+ this.webBackground.appendChild(webview)
+ this.webviewMap.set(src+this.padMapKey, webview)
+ // console.log("number of webviews",this.webviewMap.size)
+ // $("webview").each(function(i, obj) {
+ // obj.setAttribute('selected','0')
+ // })
+
+ Elements.setStyle(webview, {
+ position: "absolute",
+ overflow: "auto",
+ width: "100%",
+ height: "100%",
+ // border: "1px solid #fff"
+ })
+
+ Elements.setStyle(progressBar, {
+ position: "absolute",
+ overflow: "hidden",
+ top: "0%",
+ left: "10%",
+ visibility: "hidden",
+ background: "white",
+ filter: "blur(50px)",
+ opacity: "0.2",
+ // pointerEvents: 'none',
+ width: "40%",
+ height: "100%"
+ // opacity: "0.1"
+ })
+
+ webview.src=src
+ webview.preload= path.join(__dirname, './preloadPad.js')
+
+ let listItem=document.createElement('div')
+ listItem.className="interactiveElement"
+ listItem.classList.add("webviewListItem")
+ listItem.setAttribute('id',src+this.padMapKey)
+ // listItem.style.height = "5%"
+ // // listItem.style.width = "80%"
+ // listItem.style.marginLeft="10%"
+ // listItem.style.marginTop="5%"
+ listItem.style.padding = "10px"
+ // listItem.style.paddingBottom = "10px"
+ this.webviewList.appendChild(listItem)
+ this.listMap.set(src+this.padMapKey,listItem)
+
+ webview.setAttribute('id',src+this.padMapKey)
+
+ this.setSelectedWebview(src+this.padMapKey)
+
+ this.addNewTab(webview,src)
+
+ listItem.addEventListener('click',(e)=>{
+ console.log(e.target.getAttribute('id'))
+ this.setSelectedWebview(e.target.getAttribute('id'))
+ this.webviewList.style.visibility = "hidden"
+ $(this.tabs).animate({scrollLeft: $(".tab[id='"+e.target.getAttribute('id')+"']").prop('offsetLeft') - 10}, "slow")
+ this.enableButtons()
+ })
+
+ listItem.addEventListener('pointerenter',(e)=>{
+ e.target.style.background = "#ddd"
+ let webview=null
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ if(item.getAttribute('selected')=='0')item.style.opacity="0.5"
+ webview=item
+ // obj.style.width="50%"
+ // obj.style.height="50%"
+ }
+ })
+ this.overlayMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ }
+ })
+ // webview.getAttribute('loaded')=='0' ? this.overlay.style.visibility='visible' : this.overlay.style.visibility='hidden'
+ })
+
+ listItem.addEventListener('pointerleave',(e)=>{
+ e.target.style.background = "#fff"
+ if(e.target.getAttribute('selected')=='1') e.target.style.background = "#999"
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ if(item.getAttribute('selected')=='0'){
+ item.style.visibility="hidden"
+ }
+ if(item.getAttribute('selected')=='1'){
+ item.style.visibility="visible"
+ item.style.opacity="1"
+ }
+ // obj.style.width="100%"
+ // obj.style.height="100%"
+ })
+ })
+ let overlay = document.createElement('img')
+ overlay.style.position="absolute"
+ overlay.style.width="100%"
+ overlay.style.height="100%"
+ overlay.style.top="0px"
+ overlay.style.left="0px"
+ // overlay.style.background="white"
+ overlay.classList.add("minimalPadOverlay")
+ overlay.src="img/overlay.png"
+
+ this.overlayMap.set(src,overlay)
+
+ let loadAnim = document.createElement("div")
+ loadAnim.style.webkitAnimation= "spin 2s linear infinite"
+ loadAnim.style.animation= "spin 2s linear infinite"
+ loadAnim.style.position = "absolute"
+
+ document.styleSheets[0].insertRule("div.tabs::-webkit-scrollbar {display: none;}");
+ document.styleSheets[0].insertRule('\
+ @keyframes spin {\
+ from { transform: rotateZ(0deg); }\
+ to { transform: rotateZ(360deg); }\
+ }'
+ )
+
+ // overlay.appendChild(loadAnim)
+
+ this.webBackground.appendChild(overlay)
+ this.webBackground.appendChild(progressBar)
+
+ webview.addEventListener('did-navigate', (e) => {
+ this.enableButtons()
+ //this.backButton.style.opacity = (webview.canGoBack()) ? 1 : 0.5
+ //this.forwardButton.style.opacity = (webview.canGoForward()) ? 1 : 0.5
+ })
+
+ webview.addEventListener('dom-ready',()=>{
+ // console.log("DOM READY TABBED WEBVIEW!!!!!!!!!!")
+ listItem.innerHTML=webview.getTitle()+" "+src
+ $(".tab[id='"+src+this.padMapKey+"']").children()[0].innerHTML=webview.getTitle()
+ // $(".tab[id='"+src+"']").children()[0].innerHTML="READY LOADED"
+ $(this.tabs).animate({scrollLeft: $(".tab[id='"+src+"']").prop('offsetLeft') - 10}, "slow")
+ })
+
+ webview.addEventListener('ipc-message', (e) => {
+ if(e.channel=='webviewPointerDown')this.scatter.bringToFront()
+ })
+
+ let obj = { blur: 2 }
+ let loadAnimation = new TimelineMax({repeat:-1})
+ // loadAnimation.add( TweenMax.to(obj, 0.5, {blur: 5,onUpdate: () => {web.style.filter = "blur(" + obj.blur + "px)"}}) )
+ // loadAnimation.add( TweenMax.to(obj, 0.5, {blur: 2,onUpdate: () => {web.style.filter = "blur(" + obj.blur + "px)"}}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0.5, {left:"60%"}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0, {left:"20%"}) )
+ loadAnimation.add( TweenMax.to(progressBar, 0.5, {left:"60%"}) )
+ loadAnimation.stop()
+
+ webview.addEventListener('did-start-loading', ()=>{
+ console.log("started loading page")
+
+ webview.setAttribute('loaded','0')
+ overlay.style.visibility="visible"
+ webview.style.filter = "blur(10px)"
+ progressBar.style.visibility="visible"
+ loadAnimation.play()
+
+ let w = overlay.offsetWidth
+ let h = overlay.offsetHeight
+
+ /*let animationSize = w{
+ loadAnimation.stop()
+ webview.style.filter = "blur(0px)"
+ webview.setAttribute('loaded','1')
+ this.layout()
+ progressBar.style.visibility="hidden"
+ overlay.style.visibility="hidden"
+ })
+
+ webview.addEventListener('new-window', (e) => {
+ if (!this.webviewMap.has(e.url+this.padMapKey)){
+ this.addNewWebview(e.url)
+ }
+ })
+ this.id++
+ }
+
+ setSelectedWebview(id){
+ let webview=null
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ item.setAttribute('selected','0')
+ item.style.opacity = "0"
+ item.style.visibility = "hidden"
+ if(key==id){
+ webview=item
+ item.setAttribute('selected','1')
+ item.style.opacity = "1"
+ item.style.visibility = "visible"
+ }
+ })
+
+ this.tabMap.forEach(function (tab, key, mapObj) {
+ tab.style.border="none"
+ if(key==id){
+ tab.style.borderLeft="1px solid black"
+ tab.style.borderTop="1px solid black"
+ tab.style.borderRight="1px solid black"
+ }
+ })
+
+ this.listMap.forEach(function (entry, key, mapObj) {
+ entry.style.background = "white"
+ entry.setAttribute('selected','0')
+ if(key==id){
+ entry.style.background = "#999"
+ entry.setAttribute('selected','1')
+ }
+ })
+ this.web = webview
+ // webview.getAttribute('loaded')=='0' ? this.overlay.style.visibility='visible' : this.overlay.style.visibility='hidden'
+ }
+
+ addNewTab(webview,src){
+
+ this.tabMap.forEach(function (tab, key, mapObj) {
+ tab.style.border="none"
+ })
+ let tab=document.createElement('div')
+ tab.className="tab"
+ tab.classList.add("interactiveElement")
+ tab.style.display="flex"
+ tab.style.flexFlow="row nowrap"
+ tab.style.textOverflow="ellipsis"
+ tab.setAttribute('id',src+this.padMapKey)
+ // tab.innerHTML=" "
+ tab.style.color="black"
+ tab.style.background="white"
+ tab.style.borderLeft="1px solid black"
+ tab.style.borderTop="1px solid black"
+ tab.style.borderRight="1px solid black"
+ tab.style.position="relative"
+ tab.style.borderRadius="5px 5px 0 0"
+ tab.style.maxWidth="25%"
+ tab.style.height="80%"
+ tab.style.alignItems= "center"
+ // tab.style.display="inline"
+ // tab.style.marginLeft="10px"
+ tab.style.paddingLeft="10px"
+ this.tabs.appendChild(tab)
+ this.tabMap.set(src+this.padMapKey,tab)
+ let newSelectedWebview=null
+
+ let title = document.createElement('div')
+ title.classList.add('tabTitle')
+ // title.style.display="flex"
+ // title.style.flexFlow="row nowrap"
+ title.style.overflow="hidden"
+ title.style.whiteSpace="nowrap"
+ title.style.textOverflow="ellipsis"
+ title.style.pointerEvents="none"
+ title.innerHTML="new Tab"
+ tab.appendChild(title)
+
+ let close = document.createElement('img')
+ close.setAttribute('id',src+this.padMapKey)
+ close.classList.add('tabCloseButton')
+ close.style.background="#666"
+ close.style.height="50%"
+ close.style.marginLeft="10px"
+ close.style.marginRight="10px"
+ close.style.borderRadius="100%"
+ close.style.visibility="hidden"
+ close.src = "../../assets/icons/svg/cross.svg"
+ tab.appendChild(close)
+
+ close.addEventListener('click',(e)=>{
+ e.stopPropagation()
+ e.preventDefault()
+ let newSelectedItem = false
+
+ if(this.webviewMap.size>1){
+ let next=this.getNextTab(e.target.getAttribute("id"))
+ let previous=this.getPreviousTab(e.target.getAttribute("id"))
+
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ if(item.getAttribute('selected')=='1'){
+ newSelectedItem=true
+ }
+ mapObj.delete(key)
+ $(item).remove()
+ }
+ })
+
+ this.tabMap.forEach(function (tab, key, mapObj) {
+ if(tab.getAttribute('id')==e.target.getAttribute("id")){
+ mapObj.delete(key)
+ $(tab).remove()
+ }
+ })
+
+ this.listMap.forEach(function (entry, key, mapObj) {
+ if(entry.getAttribute('id')==e.target.getAttribute("id")){
+ mapObj.delete(key)
+ }
+ })
+ $(".webviewListItem[id='"+e.target.getAttribute("id")+"']").remove()
+ $(e.target).remove()
+ if(newSelectedItem){
+ next!=null ? this.setSelectedWebview(next.getAttribute('id')) : this.setSelectedWebview(previous.getAttribute('id'))
+ }
+ }
+ })
+
+ tab.addEventListener('pointerenter',(e)=>{
+ if(this.webviewMap.size>1)e.target.childNodes[1].style.visibility="visible"
+ let z=this.frame.style.zIndex
+
+ let webview=null
+ e.target.style.background="#ddd"
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ if(item.getAttribute('selected')=='0')item.style.opacity="0.5"
+ webview=item
+ // obj.style.width="50%"
+ // obj.style.height="50%"
+ }
+ })
+ this.overlayMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ }
+ })
+ // webview.getAttribute('loaded')=='0' ? this.overlay.style.visibility='visible' : this.overlay.style.visibility='hidden'
+ /*Popup.open({"text":webview.getTitle()},
+ {x: e.clientX, y: e.clientY},
+ { fontSize: "2vh", maxWidth: width*0.2, spacing: '10px', notchPosition: 'topLeft', zIndex: z+100})*/
+ })
+
+ tab.addEventListener('pointerleave',(e)=>{
+ e.target.childNodes[1].style.visibility="hidden"
+ e.target.style.background="#fff"
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ if(item.getAttribute('selected')=='0'){
+ item.style.visibility="hidden"
+ }
+ if(item.getAttribute('selected')=='1'){
+ item.style.visibility="visible"
+ item.style.opacity="1"
+ }
+ // obj.style.width="100%"
+ // obj.style.height="100%"
+ })
+ //this.web.style.visibility="visible"
+ //this.web.style.opacity="1"
+ })
+ tab.addEventListener('click',(e)=>{
+ this.tabMap.forEach(function (item, key, mapObj) {
+ item.style.border="none"
+ })
+
+ e.target.style.borderLeft="1px solid black"
+ e.target.style.borderTop="1px solid black"
+ e.target.style.borderRight="1px solid black"
+
+ this.setSelectedWebview(e.target.getAttribute('id'))
+
+ if(e.target.offsetLeft+e.target.offsetWidth>this.tabs.offsetWidth+this.tabs.scrollLeft){
+ $(this.tabs).animate({scrollLeft: e.target.offsetLeft-this.tabs.offsetWidth+e.target.offsetWidth}, "slow")
+ }
+ if(e.target.offsetLeft{
+ this.scatter.scalable=false
+ this.element.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameTop.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameLeft.addEventListener('pointerenter',(e)=>{
+ // e.target.setPointerCapture(e.pointerId)
+ this.scatter.scalable=false
+ this.element.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameLeft.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameRight.addEventListener('pointerenter',(e)=>{
+ this.scatter.scalable=false
+ this.element.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameRight.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+ // this.element.style.overflow = "auto"
+ // this.element.style.position = "absolute"
+
+ this.layout()
+
+ this.closeButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.frame.style.visibility="hidden"
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "../../assets/icons/png/flat/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+
+ this.scatter.bringToFront()
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ this.element.addEventListener('pointerdown', (e) => {
+ this.scatter.bringToFront()
+ })
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ urlPadMap.delete(this.url)
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value=value
+ button.draggable = false
+ this.frame.appendChild(button)
+ return button
+ }
+
+ addToolButton(src, value) {
+ console.log("adding tool button",value)
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value=value
+ button.draggable = false
+ this.frame.appendChild(button)
+ this.toolButtonMap.set(value,button)
+ return button
+ }
+
+ layout() {
+ console.log("layouting minmalPadFromElement")
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+
+ let size = "calc(100% - " + (2*b) +"px)"
+ let h = "calc(100% - " + (b) +"px)"
+ let w = "calc(100% - "+2 +"px)"
+ let w2 = "calc(100% - " + (b+2) +"px)"
+
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+
+ Elements.setStyle(this.element, {
+ width: w,
+ height: h,
+ top: 0+"px",
+ left: 0+"px"})
+
+ Elements.setStyle(this.pseudoFrameTop, {
+ width: w,
+ height: 0.1*b+"px",
+ margin: "0px"})
+ Elements.setStyle(this.pseudoFrameLeft, {
+ width: 0.1*b+"px",
+ height: h,
+ margin: "0px"})
+ Elements.setStyle(this.pseudoFrameRight, {
+ width: 0.1*b+"px",
+ height: h,
+ margin: "0px"})
+
+ Elements.setStyle(this.closeButton, {
+ right: (b * 0.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ let buttonCounter=0
+ this.toolButtonMap.forEach(function (entry, key) {
+ // console.log(entry,key)
+ Elements.setStyle(entry, {
+ left: (b * (buttonCounter+0.75)) + "px",
+ bottom: "0px",
+ margin: 0.1*b+"px",
+ width: 0.8*b+"px",
+ height: 0.8*b+"px"})
+
+ buttonCounter++
+ }
+ )
+
+ /*Elements.setStyle(this.addContentButton, {
+ left: (b * 0.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.copyButton, {
+ left: (b * 1.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.pasteButton, {
+ left: (b * 2.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.saveButton, {
+ left: (b * 3.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+
+ Elements.setStyle(this.deleteButton, {
+ left: (b * 4.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})*/
+ }
+}
+
+module.exports = { Pad, minimalPad, DOMPadContainer, PadFromElement, minimalPadFromElement, PadAccordion }
diff --git a/browser/padMinimal.js b/browser/padMinimal.js
new file mode 100644
index 0000000..12be656
--- /dev/null
+++ b/browser/padMinimal.js
@@ -0,0 +1,1380 @@
+const {fileURL} = require('./utils.js')
+const path = require('path')
+
+/* A specialization that ignores webview events and thus allows
+ * webviews to get touch, mouse and wheel events.
+ */
+class minimalDOMPadContainer extends DOMScatterContainer {
+
+ capture(event) {
+ if (event.target.tagName === 'WEBVIEW' || event.target.classList.contains('interactiveElement'))
+ return false
+ return super.capture(event)
+ }
+}
+
+/* A wrapper for a webview that behaves like a virtual tablet browser.
+ * Uses a DOMScatter to zoom and rotate the virtual browser window.
+ * The position of buttons and the border size remain constant.
+ */
+class minimalPad {
+
+ constructor(scatterContainer, {
+ startScale=1.0, minScale=0.25, maxScale=10.5,
+ autoBringToFront=true,
+ type = 'minimal',
+ title="",
+ tabbedView = true,
+ hasTtitleBar = false,
+ // urlList=["https://www.iwm-tuebingen.de/www/index.html"],
+ padMapKey="key",
+ url="https://www.iwm-tuebingen.de/www/index.html",
+ hideOnStart=false,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ this.x = x
+ this.y = y
+ this.type = type
+ this.url = url
+ // this.urlList = urlList
+ this.padMapKey = padMapKey,
+ this.title = title,
+ this.hasTtitleBar = hasTtitleBar
+ this.tabbedView = tabbedView
+ this.hideOnStart = hideOnStart
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.startScale = startScale
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.web = null
+ this.WebviewCounter = 0
+ this.id=0
+ this.pointerCounter = 0
+
+ this.webviewMap = new Map()
+ this.overlayMap = new Map()
+ this.tabMap = new Map()
+ this.listMap = new Map()
+
+ this.frame = document.createElement('div')
+ this.frame.classList.add("pad")
+ this.border = 50 / startScale
+
+ Elements.setStyle(this.frame, {
+ backgroundColor: "#333",
+ position: "absolute",
+ display: 'flex',
+ width: this.width+"px",
+ height: this.height+"px",
+ visibility: "visible",
+ top: 0,
+ left: 0,
+ // boxShadow: `10px 10px 10px rgba(0, 0, 0, 0.5)`,
+ // borderRadius: '10px',
+ overflow: "visible"})
+
+ document.body.appendChild( this.frame)
+
+ this.tabs=document.createElement("div")
+ this.webBackground=document.createElement("div")
+ this.webviewList=document.createElement("div")
+ this.titlebar = document.createElement("div")
+ this.webViewSnapshot=document.createElement("img")
+
+ this.tabs.classList.add("interactiveElement")
+ this.tabs.classList.add("tabs")
+ // this.tabs.style.display="flex"
+ // this.tabs.style.flexFlow="row nowrap"
+
+ Elements.setStyle(this.tabs, {
+ position: "absolute",
+ background: "white",
+ overflowX: "auto",
+ overflowY: "hidden",
+ display: "flex",
+ flexFlow:"row nowrap",
+ // justifyContent: "flex-end",
+ alignItems: "flex-end"
+ // overflow: "auto",
+ })
+
+ this.titlebar.innerHTML = ""
+
+ Elements.setStyle(this.titlebar, {
+ color: "#fff",
+ position: "absolute",
+ overflowX: "hidden"
+ })
+
+ Elements.setStyle(this.webBackground, {
+ position: "absolute",
+ overflow: "hidden",
+ background: "white"
+ })
+
+ this.webviewList.classList.add("interactiveElement")
+
+ Elements.setStyle(this.webviewList, {
+ position: "absolute",
+ visibility: "hidden",
+ overflow: "auto",
+ background: "white",
+ boxShadow: "5px 5px 10px #bbb"
+ })
+
+ /*Elements.setStyle(this.overlay, {
+ position: "absolute",
+ overflow: "auto",
+ background: "white",
+ opacity: "0.8"
+ })*/
+
+ Elements.setStyle(this.webViewSnapshot, {
+ position: "absolute",
+ overflow: "auto"
+ })
+
+ if(this.hasTtitleBar)this.frame.appendChild(this.titlebar)
+ this.frame.appendChild(this.webBackground)
+ // this.frame.appendChild(this.webViewSnapshot)
+ this.frame.appendChild(this.tabs)
+ this.frame.appendChild(this.webviewList)
+
+ this.webViewSnapshot.style.visibility="hidden"
+
+ this.closeButton = this.addButton("../assets/icons/svg/cross.svg", "close")
+ this.backButton = this.addButton("../assets/icons/svg/left.svg", "go back")
+ this.forwardButton = this.addButton("../assets/icons/svg/right.svg", "go forward")
+ this.showWebviewListButton = this.addButton("../assets/icons/svg/flat/ic_list_48px.svg", "show list")
+
+ this.addNewWebview(this.url)
+
+ this.backButton.style.opacity = 0.5
+ this.forwardButton.style.opacity = 0.5
+
+ /*for (let callback of window.padLoadedHandler) {
+ callback(this, url)
+ }*/
+
+ // this.pseudoFrame = document.createElement('div')
+ this.pseudoFrameTop = document.createElement('div')
+ this.pseudoFrameLeft = document.createElement('div')
+ this.pseudoFrameRight = document.createElement('div')
+
+ /*Elements.setStyle(this.pseudoFrame, {
+ position: "absolute",
+ top: "0px",
+ left: "0px",
+ width: "100%",
+ height: "100%",
+ pointerEvents: "none"
+ })*/
+
+ Elements.setStyle(this.pseudoFrameTop, {
+ position: "absolute",
+ // background: "pink",
+ top: "0px",
+ left: "0px",
+ width: "100%"
+ // height: "50px"
+ })
+
+ Elements.setStyle(this.pseudoFrameLeft, {
+ position: "absolute",
+ // background: "pink",
+ top: "0px",
+ left: "0px",
+ // width: "5px",
+ height: "100%"
+ })
+
+ Elements.setStyle(this.pseudoFrameRight, {
+ position: "absolute",
+ // background: "pink",
+ top: "0px",
+ right: "0px",
+ // width: "5px",
+ height: "100%"
+ })
+ this.frame.appendChild(this.pseudoFrameTop)
+ this.frame.appendChild(this.pseudoFrameLeft)
+ this.frame.appendChild(this.pseudoFrameRight)
+ // this.frame.appendChild(this.pseudoFrame)
+
+ this.pseudoFrameTop.addEventListener('pointerenter',(e)=>{
+ this.scatter.scalable=false
+ this.web.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameTop.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameLeft.addEventListener('pointerenter',(e)=>{
+ // e.target.setPointerCapture(e.pointerId)
+ this.scatter.scalable=false
+ this.web.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameLeft.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameRight.addEventListener('pointerenter',(e)=>{
+ this.scatter.scalable=false
+ this.web.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameRight.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ /*this.web.addEventListener('new-window', (e) => {
+
+ if(e.url.indexOf("youtube")>-1)return
+
+ if (urlPadMap.has(e.url)) {
+ let childPad = urlPadMap.get(e.url)
+ childPad.scatter.moveTo(x, y)
+ return childPad
+ }
+ let childPad = new minimalPad(this.scatterContainer, {
+ x: this.scatter.position.x+100,
+ y: this.scatter.position.y+100,
+ url: e.url,
+ width: this.scatter.width,
+ height: this.scatter.height,
+ scalable: true,
+ rotatable: true})
+ urlPadMap.set(e.url, childPad)
+
+ for(let callback of window.padLoadedHandler) {
+ callback(childPad, url)
+ }
+ })*/
+
+ /*this.backButton.addEventListener('click', (e)=>{
+ if(this.web.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goBack()
+ }
+ })
+
+ this.forwardButton.addEventListener('click', (e)=>{
+ if(this.web.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goForward()
+ }
+ })
+
+ this.closeButton.addEventListener('click', (e)=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })*/
+
+ InteractionMapper.on('tap',this.backButton, e => {
+ if(this.web.canGoBack()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goBack()
+ }
+ })
+
+ InteractionMapper.on('tap',this.forwardButton, e => {
+ if(this.web.canGoForward()){
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.web.goForward()
+ }
+ })
+
+ InteractionMapper.on('tap',this.closeButton, e => {
+ TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})
+ this.close()
+ })
+
+ this.showWebviewListButton.addEventListener('click', (e)=>{
+ /*TweenMax.to(e.target, 0.1, {scale:"1.1", onComplete: ()=>{
+ TweenMax.to(e.target, 0.1, {scale:"1.0"})
+ }})*/
+ this.webviewList.style.visibility=="visible" ? this.webviewList.style.visibility="hidden" : this.webviewList.style.visibility="visible"
+ })
+
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "../../assets/icons/png/flat/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+
+ this.scatter.moveTo({x, y})
+ this.scatter.bringToFront()
+
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ /*this.test = document.createElement('div')
+
+ Elements.setStyle(this.test, {
+ position: "absolute",
+ width: "400px",
+ height: "400px",
+ overflow: "auto",
+ background: "pink"
+ })
+ this.frame.appendChild(this.test)
+
+ this.test.addEventListener('click',()=>{
+ // this.frame.style.visibility="hidden"
+ console.log($( this.webBackground ).children())
+ $( this.webBackground ).children().css( "visibility", "hidden" )
+ $( this.frame ).children().css( "visibility", "hidden" )
+ })*/
+ this.heinz = document.createElement('div')
+
+ Elements.setStyle(this.heinz, {
+ position: "absolute",
+ background: "pink",
+ top: "0px",
+ left: "0px",
+ width: "100%",
+ opacity: "0.5",
+ display: "inline",
+ height: "100%"
+ })
+
+ this.heinz.addEventListener('pointerdown',(e)=>{
+ console.log('pointer down on Heinz')
+ e.target.style.display="none"
+ })
+ this.layout()
+
+ // this.webBackground.appendChild(this.heinz)
+ // this.heinz.classList.add("interactiveElement")
+
+ }
+
+ addNewWebview(src){
+ /* if(this.web!=null){
+ this.web.style.visibility="hidden"
+ this.web.setAttribute('selected','0')
+ }*/
+ let timeTouchStart = 0
+
+ let webview=document.createElement("webview")
+ this.web=webview
+ // webview.style.pointerEvents="none"
+ webview.setAttribute('loaded','0')
+ this.webBackground.appendChild(webview)
+ this.webviewMap.set(src+this.padMapKey, webview)
+ // console.log("number of webviews",this.webviewMap.size)
+ // $("webview").each(function(i, obj) {
+ // obj.setAttribute('selected','0')
+ // })
+
+ Elements.setStyle(webview, {
+ position: "absolute",
+ overflow: "hidden",
+ width: "100%",
+ height: "100%",
+ // border: "1px solid #fff"
+ })
+ webview.src=src
+ webview.preload= path.join(__dirname, './preloadPad.js')
+
+ let listItem=document.createElement('div')
+ listItem.className="interactiveElement"
+ listItem.classList.add("webviewListItem")
+ listItem.setAttribute('id',src+this.padMapKey)
+ // listItem.style.height = "5%"
+ // // listItem.style.width = "80%"
+ // listItem.style.marginLeft="10%"
+ // listItem.style.marginTop="5%"
+ listItem.style.padding = "10px"
+ // listItem.style.paddingBottom = "10px"
+ this.webviewList.appendChild(listItem)
+ this.listMap.set(src+this.padMapKey,listItem)
+
+ webview.setAttribute('id',src+this.padMapKey)
+
+ this.setSelectedWebview(src+this.padMapKey)
+
+ this.addNewTab(webview,src)
+
+ listItem.addEventListener('click',(e)=>{
+ console.log(e.target.getAttribute('id'))
+ this.setSelectedWebview(e.target.getAttribute('id'))
+ this.webviewList.style.visibility = "hidden"
+ $(this.tabs).animate({scrollLeft: $(".tab[id='"+e.target.getAttribute('id')+"']").prop('offsetLeft') - 10}, "slow")
+ this.enableButtons()
+ })
+
+ listItem.addEventListener('pointerenter',(e)=>{
+ e.target.style.background = "#ddd"
+ let webview=null
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ if(item.getAttribute('selected')=='0')item.style.opacity="0.5"
+ webview=item
+ // obj.style.width="50%"
+ // obj.style.height="50%"
+ }
+ })
+ this.overlayMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ }
+ })
+ // webview.getAttribute('loaded')=='0' ? this.overlay.style.visibility='visible' : this.overlay.style.visibility='hidden'
+ })
+
+ listItem.addEventListener('pointerleave',(e)=>{
+ e.target.style.background = "#fff"
+ if(e.target.getAttribute('selected')=='1') e.target.style.background = "#999"
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ if(item.getAttribute('selected')=='0'){
+ item.style.visibility="hidden"
+ }
+ if(item.getAttribute('selected')=='1'){
+ item.style.visibility="visible"
+ item.style.opacity="1"
+ }
+ // obj.style.width="100%"
+ // obj.style.height="100%"
+ })
+ })
+ let overlay = document.createElement('div')
+ overlay.style.position="absolute"
+ overlay.style.width="100%"
+ overlay.style.height="100%"
+ overlay.style.top="0px"
+ overlay.style.left="0px"
+ overlay.style.background="white"
+ overlay.style.opacity="0.6"
+
+ this.overlayMap.set(src,overlay)
+
+ let overlayCaptureEvents = document.createElement('div')
+ overlayCaptureEvents.style.position="absolute"
+ overlayCaptureEvents.style.background="lime"
+ overlayCaptureEvents.style.opacity="0.01"
+ overlayCaptureEvents.style.width="100%"
+ overlayCaptureEvents.style.height="100%"
+ overlayCaptureEvents.style.top="0px"
+ overlayCaptureEvents.style.left="0px"
+ // overlay.style.background="white"
+
+ overlayCaptureEvents.classList.add("interactiveElement")
+
+ overlayCaptureEvents.addEventListener('touchmove',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+ })
+
+ overlayCaptureEvents.addEventListener('pointerup',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+ let p = {x:e.clientX, y:e.clientY}
+
+ let webviewPosition = Points.fromPageToNode(webview,p)
+ // oldX = 0
+ // oldY = 0
+
+ let d = new Date()
+ console.log("delta Time",d.getTime()-timeTouchStart)
+ // if(d.getTime()-timeTouchStart<150)webview.sendInputEvent({type:'mouseUp', x: (e.clientX-webview.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-webview.getBoundingClientRect().top)/this.scatter.scale, button:'left', clickCount: 1})
+ if(d.getTime()-timeTouchStart<150)webview.sendInputEvent({type:'mouseUp', x: webviewPosition.x, y: webviewPosition.y, button:'left', clickCount: 1})
+ })
+
+ overlayCaptureEvents.addEventListener('pointerdown',(e)=>{
+ e.preventDefault()
+ e.stopPropagation()
+ this.scatter.bringToFront()
+ let p = {x:e.clientX, y:e.clientY}
+
+ let webviewPosition = Points.fromPageToNode(webview,p)
+
+ let d = new Date()
+ timeTouchStart = d.getTime()
+
+ // webview.sendInputEvent({type:'mouseDown', x: (e.clientX-webview.getBoundingClientRect().left)/this.scatter.scale, y: (e.clientY-webview.getBoundingClientRect().top)/this.scatter.scale, button:'left', clickCount: 1})
+ webview.sendInputEvent({type:'mouseDown', x: webviewPosition.x, y: webviewPosition.y, button:'left', clickCount: 1})
+ })
+
+ overlayCaptureEvents.addEventListener('pointermove',(e)=>{
+ if(e.pointerType!='mouse'){
+ let rotation = Angle.radian2degree(this.scatter.rotation);
+ rotation = (rotation + 360) % 360;
+
+ let r = Math.sqrt(Math.pow(e.movementX, 2) + Math.pow(e.movementY, 2));
+ let phi = Angle.radian2degree(Math.atan2(e.movementX, e.movementY));
+
+ phi = ((phi) + 630) % 360;
+ let rot = ((rotation + 90) + 630) % 360;
+
+ let diffAngle = ((0 + rot) + 360) % 360;
+ let phiCorrected = (phi + diffAngle + 360) % 360;
+
+ let deltaX = r * Math.cos(Angle.degree2radian(phiCorrected));
+ let deltaY = -r * Math.sin(Angle.degree2radian(phiCorrected));
+ // if(oldX!=0)deltaX = (e.clientX-oldX)
+ // if(oldY!=0)deltaY = (e.clientY-oldY)
+ // console.log("e.movementY",e.movementY)
+ // webview.sendInputEvent({type:'mouseWheel', x: 0, y: 0, deltaX: e.movementX, deltaY: e.movementY, canScroll: true })
+ webview.executeJavaScript("window.scrollTo(scrollX+"+(-1*deltaX)+", scrollY+"+ (-1*deltaY)+")")
+ // oldX = e.clientX
+ // oldY = e.clientY
+ }
+ })
+
+ overlayCaptureEvents.addEventListener('mousewheel',(e)=>{
+ console.log("mousewheel",e.deltaY)
+ // webview.sendInputEvent({type:'mouseWheel', x: 0, y: 0, deltaX: e.deltaX, deltaY: -e.deltaY, canScroll: true })
+ webview.executeJavaScript("window.scrollTo(scrollX+"+e.deltaX+", scrollY+"+ e.deltaY+")")
+ })
+
+ let loadAnim = document.createElement("div")
+ loadAnim.style.webkitAnimation= "spin 2s linear infinite"
+ loadAnim.style.animation= "spin 2s linear infinite"
+ loadAnim.style.position = "absolute"
+
+ document.styleSheets[0].insertRule("div.tabs::-webkit-scrollbar {display: none;}");
+ document.styleSheets[0].insertRule('\
+ @keyframes spin {\
+ from { transform: rotateZ(0deg); }\
+ to { transform: rotateZ(360deg); }\
+ }'
+ )
+
+ overlay.appendChild(loadAnim)
+
+ this.webBackground.appendChild(overlay)
+ if(remote.getGlobal('multiUserMode'))this.webBackground.appendChild(overlayCaptureEvents)
+
+ webview.addEventListener('did-navigate', (e) => {
+ this.enableButtons()
+ //this.backButton.style.opacity = (webview.canGoBack()) ? 1 : 0.5
+ //this.forwardButton.style.opacity = (webview.canGoForward()) ? 1 : 0.5
+ })
+
+ webview.addEventListener('dom-ready',()=>{
+ // console.log("DOM READY TABBED WEBVIEW!!!!!!!!!!")
+ listItem.innerHTML=webview.getTitle()+" "+src
+ $(".tab[id='"+src+this.padMapKey+"']").children()[0].innerHTML=webview.getTitle()
+ // $(".tab[id='"+src+"']").children()[0].innerHTML="READY LOADED"
+ $(this.tabs).animate({scrollLeft: $(".tab[id='"+src+"']").prop('offsetLeft') - 10}, "slow")
+ this.titlebar.innerHTML=this.web.getTitle()
+ // this.web.openDevTools()
+ // webview.style.overflow= "auto"
+ // webview.style.overflowY= "hidden"
+ })
+
+ webview.addEventListener('ipc-message', (e) => {
+ if(e.channel=='touchStart'){
+ // console.log("pointer down on webview",e.srcElement)
+ if(e.srcElement.src==webview.src)this.pointerCounter++
+ this.scatter.bringToFront()
+ }
+ if(e.channel=='touchEnd'){
+ // console.log("pointer up on webview")
+ if(e.srcElement.src==webview.src)this.pointerCounter--
+ }
+ if(e.channel=='webviewPointerCancel'){
+ this.heinz.style.display="inline"
+ }
+ // console.log("pointerCounter",this.pointerCounter)
+ })
+
+ /* let {ipcRenderer} = require('electron')
+
+ ipcRenderer.on('touchStartWebview' ,function(event){
+ console.log(event)
+ })*/
+
+ webview.addEventListener('did-start-loading', ()=>{
+
+ webview.setAttribute('loaded','0')
+ overlay.style.visibility="visible"
+
+ let w = overlay.offsetWidth
+ let h = overlay.offsetHeight
+
+ let animationSize = w{
+ // })
+ webview.addEventListener('did-stop-loading', ()=>{
+ webview.setAttribute('loaded','1')
+ this.layout()
+ overlay.style.visibility="hidden"
+ })
+
+ webview.addEventListener('new-window', (e) => {
+ if (!this.webviewMap.has(e.url+this.padMapKey)){
+ if(this.tabbedView)this.addNewWebview(e.url)
+ if(!this.tabbedView){
+
+ if (urlPadMap.has(e.url)) {
+ let childPad = urlPadMap.get(e.url)
+ childPad.scatter.moveTo(x, y)
+ return childPad
+ }
+ let childPad = new minimalPad(this.scatterContainer, {
+ x: this.scatter.position.x+100,
+ y: this.scatter.position.y+100,
+ url: e.url,
+ tabbedView: false,
+ hasTtitleBar: this.hasTtitleBar,
+ width: this.scatter.width,
+ height: this.scatter.height,
+ scalable: true,
+ rotatable: true})
+ urlPadMap.set(e.url, childPad)
+
+ for(let callback of window.padLoadedHandler) {
+ callback(childPad, url)
+ }
+
+ }
+ }
+ })
+ this.id++
+ }
+
+ setSelectedWebview(id){
+ let webview=null
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ item.setAttribute('selected','0')
+ item.style.opacity = "0"
+ item.style.visibility = "hidden"
+ if(key==id){
+ webview=item
+ item.setAttribute('selected','1')
+ item.style.opacity = "1"
+ item.style.visibility = "visible"
+ }
+ })
+
+ this.tabMap.forEach(function (tab, key, mapObj) {
+ tab.style.border="none"
+ if(key==id){
+ tab.style.borderLeft="1px solid black"
+ tab.style.borderTop="1px solid black"
+ tab.style.borderRight="1px solid black"
+ }
+ })
+
+ this.listMap.forEach(function (entry, key, mapObj) {
+ entry.style.background = "white"
+ entry.setAttribute('selected','0')
+ if(key==id){
+ entry.style.background = "#999"
+ entry.setAttribute('selected','1')
+ }
+ })
+ this.web = webview
+ if(webview.getAttribute('loaded')=='1') this.titlebar.innerHTML=this.web.getTitle()
+ // webview.getAttribute('loaded')=='0' ? this.overlay.style.visibility='visible' : this.overlay.style.visibility='hidden'
+ }
+
+ addNewTab(webview,src){
+
+ this.tabMap.forEach(function (tab, key, mapObj) {
+ tab.style.border="none"
+ })
+ let tab=document.createElement('div')
+ tab.className="tab"
+ tab.classList.add("interactiveElement")
+ tab.style.display="flex"
+ tab.style.flexFlow="row nowrap"
+ tab.style.textOverflow="ellipsis"
+ tab.setAttribute('id',src+this.padMapKey)
+ // tab.innerHTML=" "
+ tab.style.color="black"
+ tab.style.background="white"
+ tab.style.borderLeft="1px solid black"
+ tab.style.borderTop="1px solid black"
+ tab.style.borderRight="1px solid black"
+ tab.style.position="relative"
+ tab.style.borderRadius="5px 5px 0 0"
+ tab.style.maxWidth="25%"
+ tab.style.height="80%"
+ tab.style.alignItems= "center"
+ // tab.style.display="inline"
+ // tab.style.marginLeft="10px"
+ tab.style.paddingLeft="10px"
+ this.tabs.appendChild(tab)
+ this.tabMap.set(src+this.padMapKey,tab)
+ let newSelectedWebview=null
+
+ let title = document.createElement('div')
+ title.classList.add('tabTitle')
+ // title.style.display="flex"
+ // title.style.flexFlow="row nowrap"
+ title.style.overflow="hidden"
+ title.style.whiteSpace="nowrap"
+ title.style.textOverflow="ellipsis"
+ title.style.pointerEvents="none"
+ title.innerHTML="New Tab"
+ tab.appendChild(title)
+
+ let close = document.createElement('img')
+ close.setAttribute('id',src+this.padMapKey)
+ close.classList.add('tabCloseButton')
+ close.style.background="#666"
+ close.style.height="50%"
+ close.style.marginLeft="10px"
+ close.style.marginRight="10px"
+ close.style.borderRadius="100%"
+ close.style.visibility="hidden"
+ close.src = "../../assets/icons/svg/cross.svg"
+ tab.appendChild(close)
+
+ close.addEventListener('click',(e)=>{
+ e.stopPropagation()
+ e.preventDefault()
+ let newSelectedItem = false
+
+ if(this.webviewMap.size>1){
+ let next=this.getNextTab(e.target.getAttribute("id"))
+ let previous=this.getPreviousTab(e.target.getAttribute("id"))
+
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ if(item.getAttribute('selected')=='1'){
+ newSelectedItem=true
+ }
+ mapObj.delete(key)
+ $(item).remove()
+ }
+ })
+
+ this.tabMap.forEach(function (tab, key, mapObj) {
+ if(tab.getAttribute('id')==e.target.getAttribute("id")){
+ mapObj.delete(key)
+ $(tab).remove()
+ }
+ })
+
+ this.listMap.forEach(function (entry, key, mapObj) {
+ if(entry.getAttribute('id')==e.target.getAttribute("id")){
+ mapObj.delete(key)
+ }
+ })
+ $(".webviewListItem[id='"+e.target.getAttribute("id")+"']").remove()
+ $(e.target).remove()
+ if(newSelectedItem){
+ next!=null ? this.setSelectedWebview(next.getAttribute('id')) : this.setSelectedWebview(previous.getAttribute('id'))
+ }
+ }
+ this.layout()
+
+ })
+
+ tab.addEventListener('pointerenter',(e)=>{
+ if(this.webviewMap.size>1)e.target.childNodes[1].style.visibility="visible"
+ let z=this.frame.style.zIndex
+
+ let webview=null
+ e.target.style.background="#ddd"
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ if(item.getAttribute('selected')=='0')item.style.opacity="0.5"
+ webview=item
+ // obj.style.width="50%"
+ // obj.style.height="50%"
+ }
+ })
+ this.overlayMap.forEach(function (item, key, mapObj) {
+ item.style.visibility="hidden"
+ if(item.getAttribute('id')==e.target.getAttribute("id")){
+ item.style.visibility="visible"
+ }
+ })
+ // webview.getAttribute('loaded')=='0' ? this.overlay.style.visibility='visible' : this.overlay.style.visibility='hidden'
+ /*Popup.open({"text":webview.getTitle()},
+ {x: e.clientX, y: e.clientY},
+ { fontSize: "2vh", maxWidth: width*0.2, spacing: '10px', notchPosition: 'topLeft', zIndex: z+100})*/
+ })
+
+ tab.addEventListener('pointerleave',(e)=>{
+ e.target.childNodes[1].style.visibility="hidden"
+ e.target.style.background="#fff"
+ this.webviewMap.forEach(function (item, key, mapObj) {
+ if(item.getAttribute('selected')=='0'){
+ item.style.visibility="hidden"
+ }
+ if(item.getAttribute('selected')=='1'){
+ item.style.visibility="visible"
+ item.style.opacity="1"
+ }
+ // obj.style.width="100%"
+ // obj.style.height="100%"
+ })
+ //this.web.style.visibility="visible"
+ //this.web.style.opacity="1"
+ })
+ tab.addEventListener('click',(e)=>{
+ this.tabMap.forEach(function (item, key, mapObj) {
+ item.style.border="none"
+ })
+
+ e.target.style.borderLeft="1px solid black"
+ e.target.style.borderTop="1px solid black"
+ e.target.style.borderRight="1px solid black"
+
+ this.setSelectedWebview(e.target.getAttribute('id'))
+
+ if(e.target.offsetLeft+e.target.offsetWidth>this.tabs.offsetWidth+this.tabs.scrollLeft){
+ $(this.tabs).animate({scrollLeft: e.target.offsetLeft-this.tabs.offsetWidth+e.target.offsetWidth}, "slow")
+ }
+ if(e.target.offsetLeft 1){
+ this.tabs.style.display = "flex"
+ this.showWebviewListButton.style.display = "inline"
+
+ if(this.hasTtitleBar){
+ Elements.setStyle(this.webBackground, {
+ top: b+"px",
+ width: w,
+ height: size-b,
+ margin: "1px"})
+ Elements.setStyle(this.titlebar, {
+ display: "inline"})
+ }
+ if(!this.hasTtitleBar){
+ Elements.setStyle(this.webBackground, {
+ width: w,
+ height: size,
+ margin: "1px"})
+ }
+ }
+ if(this.tabMap.size <= 1){
+ this.tabs.style.display = "none"
+ this.showWebviewListButton.style.display = "none"
+
+ if(this.hasTtitleBar){
+ Elements.setStyle(this.webBackground, {
+ top: b+"px",
+ width: w,
+ height: size,
+ margin: "1px"})
+ Elements.setStyle(this.titlebar, {
+ display: "inline"})
+ }
+ if(!this.hasTtitleBar){
+ Elements.setStyle(this.webBackground, {
+ width: w,
+ height: h,
+ margin: "1px"})
+ }
+ }
+
+ if(!this.tabbedView){
+ this.tabs.style.display = "none"
+ this.showWebviewListButton.style.display = "none"
+ Elements.setStyle(this.webBackground, {
+ width: w,
+ height: h,
+ margin: "1px"})
+ }
+ }
+
+}
+
+class minimalPadFromElement {
+
+ constructor(element, scatterContainer, {
+ startScale=1.0, minScale=0.1, maxScale=1.0,
+ autoBringToFront=true,
+ translatable=true, scalable=true, rotatable=true,
+ movableX=true,
+ movableY=true,
+ rotationDegrees=null,
+ rotation=null,
+ onTransform=null,
+ transformOrigin = 'center center',
+ // extras which are in part needed
+ x=0,
+ y=0,
+ width=null,
+ height=null,
+ resizable=false,
+ } ={}) {
+
+ this.element = element
+
+ this.x = x
+ this.y = y
+ this.width = width
+ this.height = height
+ this.minScale = minScale
+ this.maxScale = maxScale
+ this.scatterContainer = scatterContainer
+ this.scale = startScale
+ this.scalable = scalable
+ this.rotatable = rotatable
+ this.rotationDegrees = this.startRotationDegrees
+ this.transformOrigin = transformOrigin
+
+ this.frame = document.createElement('div')
+ Elements.setStyle(this.frame, {
+ width: this.width+"px",
+ height: this.height+"px",
+ backgroundColor: "#333",
+ position: "fixed",
+ top: 0,
+ left: 0,
+ overflow: "auto"})
+
+ this.closeButton = this.addButton("../assets/icons/svg/cross.svg", "close")
+
+ document.body.appendChild( this.frame)
+ this.border = 50
+
+ this.frame.appendChild(this.element)
+
+ this.title = document.createElement("div")
+ this.title.innerHTML = ""
+ this.title.style.color = "white"
+ // this.frame.appendChild(this.title)
+
+ Elements.setStyle(this.title, {
+ position: "absolute"
+ })
+
+ // this.pseudoFrame = document.createElement('div')
+ this.pseudoFrameTop = document.createElement('div')
+ this.pseudoFrameLeft = document.createElement('div')
+ this.pseudoFrameRight = document.createElement('div')
+
+ /*Elements.setStyle(this.pseudoFrame, {
+ position: "absolute",
+ top: "0px",
+ left: "0px",
+ width: "100%",
+ height: "100%",
+ pointerEvents: "none"
+ })*/
+
+ Elements.setStyle(this.pseudoFrameTop, {
+ position: "absolute",
+ // background: "pink",
+ top: "0px",
+ left: "0px",
+ width: "100%"
+ // height: "50px"
+ })
+
+ Elements.setStyle(this.pseudoFrameLeft, {
+ position: "absolute",
+ // background: "pink",
+ top: "0px",
+ left: "0px",
+ // width: "5px",
+ height: "100%"
+ })
+
+ Elements.setStyle(this.pseudoFrameRight, {
+ position: "absolute",
+ // background: "pink",
+ top: "0px",
+ right: "0px",
+ // width: "5px",
+ height: "100%"
+ })
+ this.frame.appendChild(this.pseudoFrameTop)
+ this.frame.appendChild(this.pseudoFrameLeft)
+ this.frame.appendChild(this.pseudoFrameRight)
+ // this.frame.appendChild(this.pseudoFrame)
+
+ this.pseudoFrameTop.addEventListener('pointerenter',(e)=>{
+ this.scatter.scalable=false
+ this.element.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameTop.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameLeft.addEventListener('pointerenter',(e)=>{
+ // e.target.setPointerCapture(e.pointerId)
+ this.scatter.scalable=false
+ this.element.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameLeft.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+
+ this.pseudoFrameRight.addEventListener('pointerenter',(e)=>{
+ this.scatter.scalable=false
+ this.element.pointerEvents = "none"
+ e.target.style.cursor = "move"
+ })
+ this.pseudoFrameRight.addEventListener('pointerleave',(e)=>{
+ this.scatter.scalable=true
+ // this.frame.style.cursor = "initial"
+ })
+ // this.element.style.overflow = "auto"
+ // this.element.style.position = "absolute"
+
+ this.layout()
+
+ this.closeButton.addEventListener('click', ()=>{
+ this.frame.style.display="none"
+ })
+
+ this.scatter = new DOMScatter(this.frame, scatterContainer, {
+ x: this.x,
+ y: this.y,
+ startScale: this.startScale,
+ width: this.width,
+ height: this.height,
+ minScale: this.minScale,
+ maxScale: this.maxScale,
+ scalable: this.scalable,
+ resizable: true,
+ rotatable: this.rotatable})
+
+ let img=document.createElement("img")
+ img.style.width = "70%"
+ img.style.position = "absolute"
+ img.style.bottom = "20%"
+ img.style.right = "20%"
+ img.style.pointerEvents="none"
+ img.src = "../../assets/icons/png/flat/resize.png"
+ this.scatter.resizeButton.appendChild(img)
+
+ this.scatter.bringToFront()
+ this.scatter.addTransformEventCallback((e) => {
+ let newBorder = 50 / e.scale
+ if (newBorder !== this.border) {
+ this.border = newBorder
+ this.layout()
+ }
+ })
+
+ this.element.addEventListener('pointerdown', (e) => {
+ this.scatter.bringToFront()
+ })
+ }
+
+ close() {
+ // this.frame.style.display="none"
+ this.frame.parentNode.removeChild(this.frame)
+ urlPadMap.delete(this.url)
+ }
+
+ addButton(src, value) {
+ let button = document.createElement("img")
+ button.type = "image"
+ button.className = "frameButton"
+ button.style.position = "absolute"
+ button.src = fileURL(src)
+ button.value="close"
+ button.draggable = false
+ this.frame.appendChild(button)
+ return button
+ }
+
+ layout() {
+ let b = this.border
+ let b2 = b * 2
+ let b8 = b / 8
+ let b25 = b / 25
+ let b15 = b / 15
+
+ let size = "calc(100% - " + (2*b) +"px)"
+ let h = "calc(100% - " + (b) +"px)"
+ let w = "calc(100% - "+2 +"px)"
+ let w2 = "calc(100% - " + (b+2) +"px)"
+
+ this.scatter.resizeButton.style.width=b+"px"
+ this.scatter.resizeButton.style.height=b+"px"
+
+ Elements.setStyle(this.frame, {
+ // borderRadius: b8 + "px",
+ // boxShadow: `${b25}px ${b15}px ${b8}px rgba(0, 0, 0, 0.5)`
+ })
+
+ Elements.setStyle(this.element, {
+ width: w,
+ height: h,
+ top: 0+"px",
+ left: 0+"px"})
+
+ Elements.setStyle(this.title, {
+ left: (b * 1.5) + "px",
+ fontSize: (b * 0.8) + "px",
+ top: (0.1)+"0px"})
+
+ Elements.setStyle(this.pseudoFrameTop, {
+ width: w,
+ height: 0.1*b+"px",
+ margin: "0px"})
+ Elements.setStyle(this.pseudoFrameLeft, {
+ width: 0.1*b+"px",
+ height: h,
+ margin: "0px"})
+ Elements.setStyle(this.pseudoFrameRight, {
+ width: 0.1*b+"px",
+ height: h,
+ margin: "0px"})
+ Elements.setStyle(this.closeButton, {
+ right: (b * 0.75) + "px",
+ bottom: "0px",
+ width: b+"px",
+ height: b+"px"})
+ }
+}
+
+module.exports = { minimalPad, minimalDOMPadContainer, minimalPadFromElement}
diff --git a/browser/preload.js b/browser/preload.js
new file mode 100644
index 0000000..b34bc67
--- /dev/null
+++ b/browser/preload.js
@@ -0,0 +1,231 @@
+const { fileURL, loadScript, hideCursor, showCursor } = require('./utils.js')
+let { remote } = require('electron')
+
+const webFrame = require('electron').webFrame
+
+// UO: Disable unintended zoom of fullscreen page if user wants to zoom
+// only parts like Eyevisit info cards.
+console.log('Disable pinch zoom', webFrame)
+webFrame.setVisualZoomLevelLimits(1, 1)
+
+let padContainer = null
+let hideCursorTimeout = null
+let debug = false
+let urlPadMap = new Map()
+
+window.urlPadMap = urlPadMap
+window.padLoadedHandler = []
+window.nodeDirname = __dirname
+
+function pageSize() {
+ var w = window,
+ d = document,
+ e = d.documentElement,
+ g = d.getElementsByTagName('body')[0],
+ width = w.innerWidth || e.clientWidth || g.clientWidth,
+ height = w.innerHeight || e.clientHeight || g.clientHeight
+
+ return [width, height]
+}
+
+let size = pageSize()
+let pageWidth = size[0]
+let pageHeight = size[1]
+
+/* Open a new at x, y position. */
+function openPad(url, x, y) {
+ console.log('openPad')
+ if (remote.getGlobal('useBrowserView')) {
+ return ipcRenderer.send('loadBrowserView', { url, x, y })
+ }
+ const { Pad } = require('./pad.js')
+ const { minimalPad } = require('./padMinimal.js')
+ pad = null
+ if (urlPadMap.has(url)) {
+ let pad = urlPadMap.get(url)
+ pad.scatter.bringToFront()
+ /*TweenMax.to(pad.frame, 0.5, {
+ boxShadow: "0 0 25px 5px white", onComplete: () => {
+ TweenMax.to(pad.frame, 0.5, { boxShadow: "none" })
+ }
+ })*/
+ TweenMax.to(pad.frame, 0.2, {
+ scale: '1.01',
+ onComplete: () => {
+ TweenMax.to(pad.frame, 0.2, { scale: '1' })
+ }
+ })
+ // pad.scatter.moveTo(x, y)
+ return pad
+ }
+ y + 1600 > pageHeight ? (y = pageHeight - 1600) : (y = y)
+ if (remote.getGlobal('useMinimalPad')) {
+ pad = new minimalPad(padContainer, {
+ x: x,
+ y: y,
+ url: url,
+ tabbedView: true,
+ hasTtitleBar: false,
+ hideOnStart: false,
+ startScale: 1,
+ width: 1000,
+ height: 1500,
+ scalable: true,
+ rotatable: true
+ })
+ }
+ if (!remote.getGlobal('useMinimalPad')) {
+ pad = new Pad(padContainer, {
+ x: x,
+ y: y,
+ url: url,
+ hideOnStart: false,
+ startScale: 1,
+ width: 1000,
+ height: 1500,
+ scalable: true,
+ rotatable: true
+ })
+ }
+ urlPadMap.set(url, pad)
+ for (let callback of window.padLoadedHandler) {
+ callback(pad, url)
+ }
+}
+
+window.padLoadedHandler.push((pad, url) => {
+ console.log('Add specific behavior')
+})
+
+/* According to https://electron.atom.io/docs/faq/
+"I can not use jQuery/RequireJS/Meteor/AngularJS in Electron" we
+have to rename the symbols in the page before including other libraries.
+Remember to use nodeRequire after this point.
+*/
+window.nodeRequire = require
+delete window.require
+delete window.exports
+delete window.module
+
+/* Create a DOMPadContainer, i.e. a special DOMScatterContainer, as a wrapper
+of the document body.
+*/
+window.addEventListener('load', e => {
+ console.log('preloading')
+ // ../iwmlib/dist/iwmlib.3rdparty.js
+ // loadScript('../iwmlib/lib/3rdparty/preload.js', () => {
+ loadScript('../../iwmlib/dist/iwmlib.3rdparty.preload.js', () => {
+ //console.log("../iwmlib/dist/iwmlib.3rdparty.js loaded")
+ console.log('greensock loaded')
+ // loadScript('../iwmlib/dist/iwmlib.js', () => {
+ loadScript('../../iwmlib/dist/iwmlib.js', () => {
+ console.log('../iwmlib/dist/iwmlib.js loaded')
+
+ /* const { Pad, DOMPadContainer } = nodeRequire('./pad.js')
+ padContainer = new DOMPadContainer(document.body)
+
+ window.nodePadContainer = padContainer
+ ipcRenderer.send('padContainerLoaded') */
+
+ /*Register a handler for mousemove events. Hide the cursor a few
+ *seconds after the last move event.
+ */
+ document.body.addEventListener('mousemove', e => {
+ showCursor()
+ clearTimeout(hideCursorTimeout)
+ hideCursorTimeout = setTimeout(hideCursor, 3000)
+ })
+ })
+
+ if (debug) {
+ loadScript('../iwmlib/dist/iwmlib.pixi.js', () => {
+ console.log('../iwmlib/dist/iwmlib.pixi.js loaded')
+ const DebugApp = require('./debug.js')
+ let debugApp = new DebugApp(document.body)
+ debugApp.setup()
+ debugApp.run()
+ })
+ }
+ })
+})
+
+/* Register a handler for all click events and check whether the link target
+opens a new window. If the link target is blank, prevent the default behavior
+and create a Pad scatter object instead.
+*/
+window.addEventListener('click', e => {
+ let node = e.target
+ let url = ''
+ let target = ''
+
+ let multiUserMode = false // remote.getGlobal('multiUserMode') // DOMScatter && remote.getGlobal('multiUserMode')
+
+ // console.log("click", multiUserMode, remote.getGlobal('multiUserMode'))
+ if (multiUserMode) {
+ while (node !== null) {
+ if (node.tagName === 'A' || node.tagName === 'a') {
+ if (node.target instanceof SVGAnimatedString) {
+ url = node.href.baseVal
+ target = node.target.baseVal
+ } else {
+ url = node.href
+ target = node.target
+ }
+ if (target === '_blank') {
+ e.preventDefault()
+ openPad(url, e.clientX, e.clientY)
+ }
+ return
+ }
+ node = node.parentNode
+ }
+ }
+})
+
+/* Register a handler for contextmenu events and check whether the link target
+// opens a new window. If the link target is blank, prevent the default behavior
+and show a popup menu with the option to open a pad instead.
+*/
+window.addEventListener('contextmenu', e => {
+ let node = e.target
+ let url = null
+ if (remote.getGlobal('multiUserMode')) {
+ while (node !== null) {
+ if (node.tagName === 'A' || node.tagName === 'a') {
+ if (node.target instanceof SVGAnimatedString) {
+ url = node.href.baseVal
+ } else {
+ url = node.href
+ }
+ e.preventDefault()
+ let point = { x: e.clientX, y: e.clientY }
+ PopupMenu.open(
+ {
+ 'Open new Pad': () => openPad(url, e.clientX, e.clientY)
+ },
+ point,
+ { fontSize: '0.8em' }
+ )
+ return
+ }
+ node = node.parentNode
+ }
+ }
+})
+
+/* Special error handling if the rendere process sends a error notification
+log this error on the main console.
+*/
+let { Console } = require('console')
+let debugConsole = new Console(process.stdout, process.stderr)
+
+let { ipcRenderer } = require('electron')
+window.addEventListener('error', event => {
+ debugConsole.error('PAGE ERROR', event.error, event.filename)
+ debugConsole.error('open', window.location.href)
+ ipcRenderer.send('error', 1)
+})
+
+ipcRenderer.on('newPad', (e, data) => {
+ openPad(data, 0, 0)
+})
diff --git a/browser/preloadPad.js b/browser/preloadPad.js
new file mode 100644
index 0000000..5c24f84
--- /dev/null
+++ b/browser/preloadPad.js
@@ -0,0 +1,83 @@
+let { remote } = require('electron')
+let { ipcRenderer } = require('electron')
+
+const path = require('path')
+
+const webFrame = require('electron').webFrame
+console.log('Disable pinch zoom', webFrame)
+webFrame.setVisualZoomLevelLimits(1, 1)
+
+window.nodePath = path
+
+window.nodeDirname = __dirname
+
+window.nodeRequire = require
+delete window.require
+delete window.exports
+delete window.module
+
+window.padLoadedHandler = []
+
+let pointerCounter = 0
+
+window.addEventListener('pointerdown', (e) => {
+ //e.preventDefault()
+ // console.log("ipcRenderer.sendToHost('webviewPointerDown')")
+ ipcRenderer.sendToHost('webviewPointerDown')
+})
+
+window.addEventListener('pointerup', (e) => {
+ // console.log("ipcRenderer.sendToHost('webviewPointerUp')")
+ ipcRenderer.sendToHost('webviewPointerUp')
+})
+
+window.addEventListener('pointerenter', (e) => {
+ // console.log("ipcRenderer.sendToHost('webviewPointerEnter')")
+ ipcRenderer.sendToHost('webviewPointerEnter')
+})
+
+window.addEventListener('pointercancel', (e) => {
+ // console.log("ipcRenderer.sendToHost('webviewPointerCancel')")
+ ipcRenderer.sendToHost('webviewPointerCancel')
+})
+
+window.addEventListener('pointerleave', (e) => {
+ // console.log("ipcRenderer.sendToHost('webviewPointerLeave')")
+ ipcRenderer.sendToHost('webviewPointerLeave')
+})
+
+window.addEventListener('pointerout', (e) => {
+ // console.log("ipcRenderer.sendToHost('webviewPointerOut')")
+ ipcRenderer.sendToHost('webviewPointerOut')
+})
+
+window.addEventListener('pointerover', (e) => {
+ // console.log("ipcRenderer.sendToHost('webviewPointerOver')")
+ ipcRenderer.sendToHost('webviewPointerOver')
+})
+
+window.addEventListener('pointermove', (e) => {
+ // console.log("ipcRenderer.sendToHost('webviewPointerMove')")
+ ipcRenderer.sendToHost('webviewPointerMove')
+})
+
+window.addEventListener('touchmove', (e) => {
+ // console.log("ipcRenderer.sendToHost('touchmove')")
+ ipcRenderer.sendToHost('touchMove')
+})
+
+window.addEventListener('touchstart', (e) => {
+ pointerCounter++
+ // console.log("ipcRenderer.sendToHost('touchstart')")
+ ipcRenderer.sendToHost('touchStart')
+})
+
+window.addEventListener('touchend', (e) => {
+ pointerCounter--
+ // console.log("ipcRenderer.sendToHost('touchend')")
+ ipcRenderer.sendToHost('touchEnd')
+})
+
+ipcRenderer.on('overlayEvent', function () {
+ console.log('hello world From Preload')
+})
diff --git a/browser/store.js b/browser/store.js
new file mode 100644
index 0000000..799ca3c
--- /dev/null
+++ b/browser/store.js
@@ -0,0 +1,47 @@
+const electron = require('electron')
+const path = require('path')
+const fs = require('fs')
+
+class Store {
+
+ constructor(opts) {
+
+ // Renderer process has to get `app` module via `remote`, whereas the main process can get it directly
+ // app.getPath('userData') will return a string of the user's app data directory path.
+ const userDataPath = (electron.app || electron.remote.app).getPath('userData')
+ // We'll use the `configName` property to set the file name and path.join to bring it all together as a string
+ this.path = path.join(userDataPath, opts.configName + '.json')
+
+ this.data = parseDataFile(this.path, opts.defaults)
+ this.data = Object.assign(opts.defaults, this.data)
+ }
+
+ // This will just return the property on the `data` object
+ get(key) {
+ return this.data[key]
+ }
+
+ // ...and this will set it
+ set(key, val) {
+ this.data[key] = val
+ // Wait, I thought using the node.js' synchronous APIs was bad form?
+ // We're not writing a server so there's not nearly the same IO demand on the process
+ // Also if we used an async API and our app was quit before the asynchronous write had a chance to complete,
+ // we might lose that data. Note that in a real app, we would try/catch this.
+ fs.writeFileSync(this.path, JSON.stringify(this.data))
+ }
+}
+
+function parseDataFile(filePath, defaults) {
+ // We'll try/catch it in case the file doesn't exist yet, which will be the case on the first application run.
+ // `fs.readFileSync` will return a JSON string which we then parse into a Javascript object
+ try {
+ return JSON.parse(fs.readFileSync(filePath))
+ } catch(error) {
+ // if there was some kind of error, return the passed in defaults instead.
+ return defaults
+ }
+}
+
+// expose the class
+module.exports = Store
diff --git a/browser/test.js b/browser/test.js
new file mode 100644
index 0000000..2434a4a
--- /dev/null
+++ b/browser/test.js
@@ -0,0 +1,113 @@
+let fs = require('fs')
+let path = require('path')
+let {thumbnail} = require('./utils.js')
+
+let pairs = []
+let urlMap = new Map()
+function isFile(path) {
+ try {
+ return !fs.lstatSync(path).isDirectory()
+ } catch(e) {
+ if (e.code == 'ENOENT'){
+ return false
+ } else {
+ return false
+ }
+ }
+}
+
+function listPairs(src, dst, skipFiles=true) {
+ let dir = fs.readdirSync(src)
+ for(let name of dir) {
+ let srcPath = src + name
+ if (isFile(srcPath) && !skipFiles) {
+ if (srcPath.endsWith('.html')) {
+ let dstPath = dst + name.replace(/.html/, '.png')
+ pairs.push([srcPath, dstPath])
+ }
+ }
+ else {
+ if (srcPath.endsWith('.'))
+ continue
+ let indexPath = srcPath + path.sep + 'index.html'
+ if (isFile(indexPath)) {
+ let thumbnailPath = indexPath.replace(/index.html/, 'thumbnail.png')
+ pairs.push([indexPath, thumbnailPath])
+ }
+ }
+ }
+}
+
+function capturePage(focusedWindow, dstPath) {
+ focusedWindow.capturePage( (image) => {
+ fs.writeFile(dstPath, thumbnail(image), (err) => {
+ if (err) throw err;
+ nextLoadTest(focusedWindow)
+ })
+ })
+}
+
+function testLoading(focusedWindow, filePath, dstPath) {
+ let urlPath = filePath.replace(path.sep, '/')
+ let basePath = `${__dirname}`.replace('/browser', '')
+ let shortURL = `file://${basePath}/${urlPath}`
+ let fullURL = `file://${__dirname}/../../${urlPath}`
+ // console.log({basePath, shortURL, fullURL})
+ if (focusedWindow) {
+ urlMap.set(shortURL, dstPath)
+ focusedWindow.webContents.session.clearCache(() => console.log('Cache cleared'))
+ focusedWindow.webContents.loadURL(shortURL)
+ }
+}
+
+function listLibPairs() {
+ let src = "lib" + path.sep
+ let dst = "lib" + path.sep + 'thumbnails' + path.sep
+ listPairs(src, dst, false)
+}
+
+function listAppPairs() {
+ let src = "apps" + path.sep
+ let dst = "apps" + path.sep
+ listPairs(src, dst)
+}
+
+function listSrcPairs() {
+ let src = "src" + path.sep
+ let dst = "src" + path.sep
+ listPairs(src, dst)
+}
+
+function nextLoadTest(focusedWindow) {
+ if (global.errorCount > 0 && global.stopTestsOnError) {
+ console.log("Test aborted")
+ return
+ }
+ if (pairs.length > 0) {
+ let [file, image] = pairs.pop()
+ testLoading(focusedWindow, file, image)
+ }
+ else {
+ console.log("All thumbnails created")
+ }
+}
+
+function loadTests(focusedWindow) {
+ global.errorCount = 0
+ focusedWindow.webContents.on('did-finish-load', (e) => {
+ setTimeout(() => {
+ let url = e.sender.history[e.sender.history.length-1]
+ dstPath = urlMap.get(url)
+ capturePage(focusedWindow, dstPath)
+ }, 5000)
+ })
+
+ listLibPairs()
+ listSrcPairs()
+ listAppPairs()
+ nextLoadTest(focusedWindow)
+}
+
+module.exports = loadTests
+
+
diff --git a/browser/utils.js b/browser/utils.js
new file mode 100644
index 0000000..1fe13f7
--- /dev/null
+++ b/browser/utils.js
@@ -0,0 +1,38 @@
+
+function fileURL(src) {
+ let dir = __dirname.replace(/\\/g, '/')
+ return `file://${dir}/${src}`
+}
+
+function loadScript(src, callback) {
+ let url = fileURL(src)
+ let script = document.createElement('script')
+ script.onload = () => {
+ if (callback) {
+ callback.call(this, script)
+ }
+ }
+ script.src = url
+ document.head.appendChild(script)
+}
+
+function thumbnail(screenshot) {
+ return screenshot.resize({ width: 1024 }).toPNG()
+}
+
+let hiddenCursor = fileURL('../assets/cursor/cur0000.cur')
+let defaultCursor = fileURL('../assets/cursor/cur1054.cur')
+
+function hideCursor() {
+// console.log("hideCursor")
+ document.body.style.cursor = `url('${hiddenCursor}'), default`
+}
+
+function showCursor() {
+ document.body.style.cursor = `url('${defaultCursor}'), default`
+// console.log("showCursor")
+}
+
+module.exports = {
+ fileURL, loadScript, thumbnail, hideCursor, showCursor
+}
diff --git a/css/index.css b/css/index.css
index f4a3209..3570e22 100644
--- a/css/index.css
+++ b/css/index.css
@@ -27,6 +27,10 @@ body {
-moz-osx-font-smoothing: grayscale;
}
+h2 {
+ padding-left: 20px;
+}
+
h3 {
color: white;
padding: 4px;
@@ -163,7 +167,6 @@ div.title {
.container {
margin: 0;
padding: 0;
- margin-top: 32px;
border: 2pt #000;
min-height: 100%;
min-width: 100%;
diff --git a/lib/index.html b/lib/index.html
index 1fa1377..45c22b2 100644
--- a/lib/index.html
+++ b/lib/index.html
@@ -30,11 +30,8 @@
-
+
+
-
-
-
-
-
-
-
-
-
+
+
-
+
- margin: 0 60px;
- }
-
-
-
-
-
-
-
-
- Maps Module
- The maps module provides a handy toolkit to easily integrate maps in an application. Create a full screen map
- application by using the mapapp. Utilize the GeoLayer-system to integrate maps in an existing application.
- Draw graphics onto the map using geographical positions instead of pixel positions with the GeoGraphics.
- Or just use an Overlay to quickly draw icons for each point on a map.
-
-
-
-
-
-
-
-
-