Merge pull request #1763 from ethereum/remix-plugin-api

Remix plugin api
pull/1/head
yann300 6 years ago committed by GitHub
commit 90933b0d01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      package.json
  2. 65
      src/app.js
  3. 4
      src/app/components/local-plugin.js
  4. 37
      src/app/components/plugin-manager-component.js
  5. 2
      src/app/components/swap-panel-api.js
  6. 19
      src/app/components/swap-panel-component.js
  7. 36
      src/app/components/vertical-icons-component.js
  8. 39
      src/app/editor/SourceHighlighters.js
  9. 105
      src/app/files/browser-files-tree.js
  10. 104
      src/app/files/fileManager.js
  11. 7
      src/app/files/remixd-handle.js
  12. 481
      src/app/panels/file-panel.js
  13. 16
      src/app/panels/tab-proxy.js
  14. 7
      src/app/tabs/analysis-tab.js
  15. 39
      src/app/tabs/compile-tab.js
  16. 8
      src/app/tabs/debugger-tab.js
  17. 28
      src/app/tabs/run-tab.js
  18. 11
      src/app/tabs/settings-tab.js
  19. 9
      src/app/tabs/support-tab.js
  20. 9
      src/app/tabs/test-tab.js
  21. 22
      src/app/tabs/txlistener-module.js
  22. 73
      src/app/ui/landing-page/generate.js
  23. 78
      src/app/ui/landing-page/landing-page.js
  24. 7
      src/framingService.js
  25. 3
      src/lib/store.js
  26. 39
      src/remixAppManager.js
  27. 580
      src/universal-dapp.js

@ -64,7 +64,7 @@
},
"dependencies": {
"http-server": "0.9.0",
"remix-plugin": "0.0.1-alpha.33",
"remix-plugin": "0.0.1-alpha.39",
"remixd": "0.1.8-alpha.6"
},
"repository": {

@ -7,7 +7,6 @@ var async = require('async')
var request = require('request')
var remixLib = require('remix-lib')
var EventManager = require('./lib/events')
var EventEmitter = require('events')
var registry = require('./global/registry')
var UniversalDApp = require('./universal-dapp.js')
var UniversalDAppUI = require('./universal-dapp-ui.js')
@ -55,8 +54,10 @@ const FilePanel = require('./app/panels/file-panel')
import PanelsResize from './lib/panels-resize'
import { EntityStore } from './lib/store'
import { RemixAppManager } from './remixAppManager'
import { generateHomePage, homepageProfile } from './app/ui/landing-page/generate'
import { LandingPage } from './app/ui/landing-page/landing-page'
import framingService from './framingService'
import { ApiFactory } from 'remix-plugin'
import { TxListenerModule } from './app/tabs/txlistener-module'
var css = csjs`
html { box-sizing: border-box; }
@ -116,8 +117,9 @@ var css = csjs`
}
`
class App {
class App extends ApiFactory {
constructor (api = {}, events = {}, opts = {}) {
super()
var self = this
this.event = new EventManager()
self._components = {}
@ -173,7 +175,7 @@ class App {
run.apply(self)
}
profile () {
get profile () {
return {
name: 'app',
description: 'service - provides information about current context (network).',
@ -373,26 +375,13 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
/*
that proxy is used by appManager to broadcast new transaction event
*/
const txListenerModuleProxy = {
event: new EventEmitter(),
profile () {
return {
name: 'txListener',
displayName: 'transaction listener',
events: ['newTransaction'],
description: 'service - notify new transactions'
}
}
}
txlistener.event.register('newTransaction', (tx) => {
txListenerModuleProxy.event.emit('newTransaction', tx)
})
const txListenerModule = new TxListenerModule(txlistener)
txlistener.startListening()
// TODO: There are still a lot of dep between editorpanel and filemanager
let appStore = new EntityStore('module', { actives: [], ids: [], entities: {} })
let appStore = new EntityStore('module', 'name')
const appManager = new RemixAppManager(appStore)
registry.put({api: appManager, name: 'appmanager'})
@ -458,33 +447,35 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
let settings = new SettingsTab(self._components.registry)
let analysis = new AnalysisTab(registry)
let debug = new DebuggerTab()
const landingPage = new LandingPage(appManager, appStore)
// let support = new SupportTab()
let test = new TestTab(self._components.registry, compileTab)
let sourceHighlighters = registry.get('editor').api.sourceHighlighters
let configProvider = self._components.filesProviders['config']
appManager.init([
{ profile: homepageProfile(), api: generateHomePage(appManager, appStore) },
{ profile: this.profile(), api: this },
{ profile: udapp.profile(), api: udapp },
{ profile: fileManager.profile(), api: fileManager },
{ profile: sourceHighlighters.profile(), api: sourceHighlighters },
{ profile: configProvider.profile(), api: configProvider },
{ profile: txListenerModuleProxy.profile(), api: txListenerModuleProxy },
{ profile: filePanel.profile(), api: filePanel },
this.api(),
landingPage.api(),
udapp.api(),
fileManager.api(),
sourceHighlighters.api(),
configProvider.api(),
txListenerModule.api(),
filePanel.api(),
// { profile: support.profile(), api: support },
{ profile: settings.profile(), api: settings },
{ profile: pluginManagerComponent.profile(), api: pluginManagerComponent }])
settings.api(),
pluginManagerComponent.api()
])
appManager.registerMany([
{ profile: compileTab.profile(), api: compileTab },
{ profile: run.profile(), api: run },
{ profile: debug.profile(), api: debug },
{ profile: analysis.profile(), api: analysis },
{ profile: test.profile(), api: test },
{ profile: filePanel.remixdHandle.profile(), api: filePanel.remixdHandle }
compileTab.api(),
run.api(),
debug.api(),
analysis.api(),
test.api(),
filePanel.remixdHandle.api(),
...appManager.plugins()
])
appManager.registerMany(appManager.plugins())
framingService.start(appStore, swapPanelApi, verticalIconsApi, mainPanelApi, this._components.resizeFeature)
@ -512,7 +503,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
var txLogger = new TxLogger() // eslint-disable-line
txLogger.event.register('debuggingRequested', (hash) => {
if (!appStore.isActive('debugger')) appManager.activateOne('debugger')
appStore.getOne('debugger').api.debugger().debug(hash)
debug.debugger().debug(hash)
verticalIconsApi.select('debugger')
})

@ -6,7 +6,7 @@ module.exports = class LocalPlugin {
/**
* Open a modal to create a local plugin
* @param {{profile: any, api: any}[]} plugins The list of the plugins in the store
* @param {PluginApi[]} plugins The list of the plugins in the store
* @returns {Promise<{api: any, profile: any}>} A promise with the new plugin profile
*/
open (plugins) {
@ -90,7 +90,7 @@ module.exports = class LocalPlugin {
/**
* The form to create a local plugin
* @param {Profile[]} plugins Liste of profile of the plugins
* @param {ProfileApi[]} plugins Liste of profile of the plugins
*/
form (plugins = []) {
const name = this.profile.name || ''

@ -2,7 +2,7 @@ const yo = require('yo-yo')
const csjs = require('csjs-inject')
const EventEmitter = require('events')
const LocalPlugin = require('./local-plugin')
import { Plugin } from 'remix-plugin'
import { Plugin, ApiFactory } from 'remix-plugin'
const css = csjs`
.pluginSearch {
@ -34,9 +34,10 @@ const css = csjs`
}
`
class PluginManagerComponent {
class PluginManagerComponent extends ApiFactory {
constructor () {
super()
this.event = new EventEmitter()
this.views = {
root: null,
@ -46,7 +47,7 @@ class PluginManagerComponent {
this.filter = ''
}
profile () {
get profile () {
return {
displayName: 'plugin manager',
name: 'pluginManager',
@ -66,15 +67,15 @@ class PluginManagerComponent {
this.store = store
this.store.event.on('activate', (name) => { this.reRender() })
this.store.event.on('deactivate', (name) => { this.reRender() })
this.store.event.on('add', (entity) => { this.reRender() })
this.store.event.on('remove', (entity) => { this.reRender() })
this.store.event.on('add', (api) => { this.reRender() })
this.store.event.on('remove', (api) => { this.reRender() })
}
renderItem (name) {
const mod = this.store.getOne(name)
if (!mod) return
const api = this.store.getOne(name)
if (!api) return
const isActive = this.store.actives.includes(name)
const displayName = (mod.profile.displayName) ? mod.profile.displayName : name
const displayName = (api.profile.displayName) ? api.profile.displayName : name
const activationButton = isActive
? yo`
@ -92,7 +93,7 @@ class PluginManagerComponent {
<h6 class="${css.displayName}">${displayName}</h6>
${activationButton}
</div>
<p class="${css.description}">${mod.profile.description}</p>
<p class="${css.description}">${api.profile.description}</p>
</article>
`
}
@ -107,9 +108,7 @@ class PluginManagerComponent {
try {
const profile = await this.localPlugin.open(this.store.getAll())
if (!profile) return
const resolveLocaton = (iframe) => this.appManager.resolveLocation(profile, iframe)
const api = new Plugin(profile, { resolveLocaton })
this.appManager.registerOne({profile, api})
this.appManager.registerOne(new Plugin(profile))
this.appManager.activateOne(profile.name)
} catch (err) {
// TODO : Use an alert to handle this error instead of a console.log
@ -119,11 +118,11 @@ class PluginManagerComponent {
render () {
// Filtering helpers
const isFiltered = ({profile}) => profile.name.toLowerCase().includes(this.filter)
const isFiltered = (api) => api.name.toLowerCase().includes(this.filter)
const isNotRequired = ({profile}) => !profile.required
const sortByName = (a, b) => {
const nameA = a.profile.name.toUpperCase()
const nameB = b.profile.name.toUpperCase()
const nameA = a.name.toUpperCase()
const nameB = b.name.toUpperCase()
return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0
}
@ -132,10 +131,10 @@ class PluginManagerComponent {
.filter(isFiltered)
.filter(isNotRequired)
.sort(sortByName)
.reduce(({actives, inactives}, {profile}) => {
return this.store.actives.includes(profile.name)
? { actives: [...actives, profile.name], inactives }
: { inactives: [...inactives, profile.name], actives }
.reduce(({actives, inactives}, api) => {
return this.store.actives.includes(api.name)
? { actives: [...actives, api.name], inactives }
: { inactives: [...inactives, api.name], actives }
}, { actives: [], inactives: [] })
const activeTile = actives.length !== 0

@ -5,7 +5,7 @@ class SwapPanelApi {
this.event = new EventEmmitter()
this.component = swapPanelComponent
this.currentContent
verticalIconsComponent.event.on('showContent', (moduleName) => {
verticalIconsComponent.events.on('showContent', (moduleName) => {
if (!swapPanelComponent.contents[moduleName]) return
if (this.currentContent === moduleName) {
this.event.emit('toggle', moduleName)

@ -11,15 +11,10 @@ class SwapPanelComponent {
// name of the current displayed content
this.currentNode
appManager.event.on('pluginNeedsLocation', (profile, domEl) => {
if ((profile.prefferedLocation === this.name) || (!profile.prefferedLocation && opt.default)) {
this.add(profile.name, domEl)
}
})
this.store.event.on('activate', (name) => {
const { profile, api } = this.store.getOne(name)
if (((profile.prefferedLocation === this.name) || (!profile.prefferedLocation && opt.default)) &&
const api = this.store.getOne(name)
const profile = api.profile
if (((profile.location === this.name) || (!profile.location && opt.default)) &&
profile.icon && api.render && typeof api.render === 'function') {
this.add(name, api.render())
}
@ -28,8 +23,8 @@ class SwapPanelComponent {
this.store.event.on('deactivate', (name) => {
if (this.contents[name]) this.remove(name)
})
this.store.event.on('add', (entity) => { })
this.store.event.on('remove', (entity) => { })
this.store.event.on('add', (api) => { })
this.store.event.on('remove', (api) => { })
}
showContent (moduleName) {
@ -40,8 +35,8 @@ class SwapPanelComponent {
}
this.contents[moduleName].style.display = 'block'
this.currentNode = moduleName
var item = this.store.getOne(moduleName)
this.header.innerHTML = item.profile ? item.profile.displayName : ' - '
var api = this.store.getOne(moduleName)
this.header.innerHTML = api.profile ? api.profile.displayName : ' - '
return
}
}

@ -1,14 +1,14 @@
var yo = require('yo-yo')
var csjs = require('csjs-inject')
const EventEmmitter = require('events')
const EventEmitter = require('events')
// Component
class VerticalIconComponent {
constructor (name, appStore) {
this.store = appStore
this.event = new EventEmmitter()
this.events = new EventEmitter()
this.icons = {}
this.iconKind = {}
this.name = name
@ -16,28 +16,34 @@ class VerticalIconComponent {
this.store.event.on('activate', (name) => {
const { profile } = this.store.getOne(name)
if (!profile.icon) return
if (profile.prefferedLocation === this.name || !profile.prefferedLocation) {
if (profile.location === this.name || !profile.location) {
this.addIcon(profile)
}
})
this.store.event.on('deactivate', (name) => {
const item = this.store.getOne(name)
if (item && this.icons[name]) this.removeIcon(item.profile)
const api = this.store.getOne(name)
if (api && this.icons[name]) this.removeIcon(api.profile)
})
this.store.event.on('add', (entity) => { })
this.store.event.on('remove', (entity) => { })
this.store.event.on('add', (api) => { })
this.store.event.on('remove', (api) => { })
}
addIcon (mod) {
let kind = mod.kind || 'other'
this.icons[mod.name] = yo`<div class="${css.icon}" onclick=${(e) => { this._iconClick(mod.name) }} title=${mod.name} ><img src="${mod.icon}" alt="${mod.name}" /></div>`
/**
* Add an icon to the map
* @param {ModuleProfile} profile The profile of the module
*/
addIcon ({kind, name, icon}) {
this.icons[name] = yo`<div class="${css.icon}" onclick="${(e) => { this._iconClick(name) }}" title="${name}" ><img src="${icon}" alt="${name}" /></div>`
this.iconKind[kind].appendChild(this.icons[mod.name])
this.iconKind[kind || 'other'].appendChild(this.icons[name])
}
removeIcon (mod) {
let kind = mod.kind || 'other'
if (this.icons[mod.name]) this.iconKind[kind].removeChild(this.icons[mod.name])
/**
* Remove an icon from the map
* @param {ModuleProfile} profile The profile of the module
*/
removeIcon ({kind, name}) {
if (this.icons[name]) this.iconKind[kind || 'other'].removeChild(this.icons[name])
}
select (name) {
@ -53,7 +59,7 @@ class VerticalIconComponent {
let activate = this.view.querySelector(`[title="${name}"]`)
if (activate) activate.classList.toggle(`${css.active}`)
}
this.event.emit('showContent', name)
this.events.emit('showContent', name)
}
_iconClick (name) {

@ -1,13 +1,16 @@
'use strict'
const SourceHighlighter = require('./sourceHighlighter')
class SourceHighlighters {
import { ApiFactory } from 'remix-plugin'
class SourceHighlighters extends ApiFactory {
constructor () {
super()
this.highlighters = {}
}
profile () {
get profile () {
return {
displayName: 'source highlighters',
name: 'sourceHighlighters',
@ -16,27 +19,21 @@ class SourceHighlighters {
}
}
// TODO what to do with mod?
async highlight (mod, lineColumnPos, filePath, hexColor) {
return new Promise((resolve, reject) => {
let position
try {
position = JSON.parse(lineColumnPos)
} catch (e) {
throw e
}
if (!this.highlighters[mod]) this.highlighters[mod] = new SourceHighlighter()
this.highlighters[mod].currentSourceLocation(null)
this.highlighters[mod].currentSourceLocationFromfileName(position, filePath, hexColor)
resolve()
})
highlight (lineColumnPos, filePath, hexColor) {
const { from } = this.currentRequest
try {
const position = JSON.parse(lineColumnPos)
if (!this.highlighters[from]) this.highlighters[from] = new SourceHighlighter()
this.highlighters[from].currentSourceLocation(null)
this.highlighters[from].currentSourceLocationFromfileName(position, filePath, hexColor)
} catch (e) {
throw e
}
}
async discardHighlight (mod) {
return new Promise((resolve, reject) => {
if (this.highlighters[mod]) this.highlighters[mod].currentSourceLocation(null)
resolve()
})
discardHighlight () {
const { from } = this.currentRequest
if (this.highlighters[from]) this.highlighters[from].currentSourceLocation(null)
}
}

@ -2,21 +2,35 @@
var EventManager = require('../../lib/events')
function FilesTree (name, storage) {
var self = this
var event = new EventManager()
this.event = event
this.type = name
this.structFile = '.' + name + '.tree'
this.tree = {}
this.exists = function (path, cb) {
import { ApiFactory } from 'remix-plugin'
class FilesTree extends ApiFactory {
constructor (name, storage) {
super()
this.event = new EventManager()
this.storage = storage
this.type = name
this.structFile = '.' + name + '.tree'
this.tree = {}
}
get profile () {
// TODO should make them promisable
return {
name: this.type,
methods: ['get', 'set', 'remove'],
description: 'service - read/write file to the `config` explorer without need of additionnal permission.'
}
}
exists (path, cb) {
cb(null, this._exists(path))
}
function updateRefs (path, type) {
updateRefs (path, type) {
var split = path.split('/') // this should be unprefixed path
var crawlpath = self.tree
var crawlpath = this.tree
var intermediatePath = ''
split.forEach((pathPart, index) => {
intermediatePath += pathPart
@ -30,91 +44,90 @@ function FilesTree (name, storage) {
delete crawlpath[intermediatePath]
}
})
storage.set(self.structFile, JSON.stringify(self.tree))
this.storage.set(this.structFile, JSON.stringify(this.tree))
}
this._exists = function (path) {
_exists (path) {
var unprefixedpath = this.removePrefix(path)
return storage.exists(unprefixedpath)
return this.storage.exists(unprefixedpath)
}
this.init = function (cb) {
var tree = storage.get(this.structFile)
init (cb) {
var tree = this.storage.get(this.structFile)
this.tree = tree ? JSON.parse(tree) : {}
if (cb) cb()
}
this.get = function (path, cb) {
get (path, cb) {
var unprefixedpath = this.removePrefix(path)
var content = storage.get(unprefixedpath)
var content = this.storage.get(unprefixedpath)
if (cb) {
cb(null, content)
}
return content
}
this.set = function (path, content, cb) {
set (path, content, cb) {
var unprefixedpath = this.removePrefix(path)
updateRefs(unprefixedpath, 'add')
var exists = storage.exists(unprefixedpath)
if (!storage.set(unprefixedpath, content)) {
this.updateRefs(unprefixedpath, 'add')
var exists = this.storage.exists(unprefixedpath)
if (!this.storage.set(unprefixedpath, content)) {
if (cb) cb('error updating ' + path)
return false
}
if (!exists) {
event.trigger('fileAdded', [this.type + '/' + unprefixedpath, false])
this.event.trigger('fileAdded', [this.type + '/' + unprefixedpath, false])
} else {
event.trigger('fileChanged', [this.type + '/' + unprefixedpath])
this.event.trigger('fileChanged', [this.type + '/' + unprefixedpath])
}
if (cb) cb()
return true
}
this.addReadOnly = function (path, content) {
addReadOnly (path, content) {
return this.set(path, content)
}
this.isReadOnly = function (path) {
isReadOnly (path) {
return false
}
this.remove = function (path) {
remove (path) {
var unprefixedpath = this.removePrefix(path)
updateRefs(unprefixedpath, 'remove')
this.updateRefs(unprefixedpath, 'remove')
if (!this._exists(unprefixedpath)) {
return false
}
if (!storage.remove(unprefixedpath)) {
if (!this.storage.remove(unprefixedpath)) {
return false
}
event.trigger('fileRemoved', [this.type + '/' + unprefixedpath])
this.event.trigger('fileRemoved', [this.type + '/' + unprefixedpath])
return true
}
this.rename = function (oldPath, newPath, isFolder) {
rename (oldPath, newPath, isFolder) {
var unprefixedoldPath = this.removePrefix(oldPath)
var unprefixednewPath = this.removePrefix(newPath)
updateRefs(unprefixedoldPath, 'remove')
updateRefs(unprefixednewPath, 'add')
if (storage.exists(unprefixedoldPath)) {
if (!storage.rename(unprefixedoldPath, unprefixednewPath)) {
this.updateRefs(unprefixedoldPath, 'remove')
this.updateRefs(unprefixednewPath, 'add')
if (this.storage.exists(unprefixedoldPath)) {
if (!this.storage.rename(unprefixedoldPath, unprefixednewPath)) {
return false
}
event.trigger('fileRenamed', [this.type + '/' + unprefixedoldPath, this.type + '/' + unprefixednewPath, isFolder])
this.event.trigger('fileRenamed', [this.type + '/' + unprefixedoldPath, this.type + '/' + unprefixednewPath, isFolder])
return true
}
return false
}
this.resolveDirectory = function (path, callback) {
var self = this
resolveDirectory (path, callback) {
if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [self.type]: { } })
if (!path) return callback(null, { [this.type]: { } })
var tree = {}
path = self.removePrefix(path)
path = this.removePrefix(path)
var split = path.split('/') // this should be unprefixed path
var crawlpath = self.tree
var crawlpath = this.tree
split.forEach((pathPart, index) => {
if (crawlpath[pathPart]) crawlpath = crawlpath[pathPart]
})
@ -125,20 +138,12 @@ function FilesTree (name, storage) {
callback(null, tree)
}
this.removePrefix = function (path) {
removePrefix (path) {
path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path
if (path[0] === '/') return path.substring(1)
return path
}
this.profile = function () {
// TODO should make them promisable
return {
name: this.type,
methods: ['get', 'set', 'remove'],
description: 'service - read/write file to the `config` explorer without need of additionnal permission.'
}
}
}
module.exports = FilesTree

@ -3,14 +3,16 @@
const EventEmitter = require('events')
var globalRegistry = require('../../global/registry')
var CompilerImport = require('../compiler/compiler-imports')
import { ApiFactory } from 'remix-plugin'
/*
attach to files event (removed renamed)
trigger: currentFileChanged
*/
class FileManager {
class FileManager extends ApiFactory {
constructor (localRegistry) {
super()
this.openedFiles = {} // list all opened files
this.events = new EventEmitter()
this._components = {}
@ -19,30 +21,29 @@ class FileManager {
}
init () {
var self = this
self._deps = {
editor: self._components.registry.get('editor').api,
config: self._components.registry.get('config').api,
browserExplorer: self._components.registry.get('fileproviders/browser').api,
localhostExplorer: self._components.registry.get('fileproviders/localhost').api,
configExplorer: self._components.registry.get('fileproviders/config').api,
gistExplorer: self._components.registry.get('fileproviders/gist').api,
filesProviders: self._components.registry.get('fileproviders').api
this._deps = {
editor: this._components.registry.get('editor').api,
config: this._components.registry.get('config').api,
browserExplorer: this._components.registry.get('fileproviders/browser').api,
localhostExplorer: this._components.registry.get('fileproviders/localhost').api,
configExplorer: this._components.registry.get('fileproviders/config').api,
gistExplorer: this._components.registry.get('fileproviders/gist').api,
filesProviders: this._components.registry.get('fileproviders').api
}
self._deps.browserExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
self._deps.localhostExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
self._deps.configExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
self._deps.gistExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
self._deps.browserExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
self._deps.localhostExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
self._deps.configExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
self._deps.gistExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
self._deps.localhostExplorer.event.register('errored', (event) => { this.removeTabsOf(self._deps.localhostExplorer) })
self._deps.localhostExplorer.event.register('closed', (event) => { this.removeTabsOf(self._deps.localhostExplorer) })
this._deps.browserExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
this._deps.localhostExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
this._deps.configExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
this._deps.gistExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
this._deps.browserExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
this._deps.localhostExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
this._deps.configExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
this._deps.gistExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
this._deps.localhostExplorer.event.register('errored', (event) => { this.removeTabsOf(this._deps.localhostExplorer) })
this._deps.localhostExplorer.event.register('closed', (event) => { this.removeTabsOf(this._deps.localhostExplorer) })
}
profile () {
get profile () {
return {
displayName: 'file manager',
name: 'fileManager',
@ -53,10 +54,9 @@ class FileManager {
}
fileRenamedEvent (oldName, newName, isFolder) {
var self = this
if (!isFolder) {
self._deps.config.set('currentFile', '')
self._deps.editor.discard(oldName)
this._deps.config.set('currentFile', '')
this._deps.editor.discard(oldName)
if (this.openedFiles[oldName]) {
delete this.openedFiles[oldName]
this.openedFiles[newName] = newName
@ -69,7 +69,7 @@ class FileManager {
var newAbsolutePath = k.replace(oldName, newName)
this.openedFiles[newAbsolutePath] = newAbsolutePath
delete this.openedFiles[k]
if (self._deps.config.get('currentFile') === k) {
if (this._deps.config.get('currentFile') === k) {
newFocus = newAbsolutePath
}
}
@ -105,8 +105,7 @@ class FileManager {
}
currentPath () {
var self = this
var currentFile = self._deps.config.get('currentFile')
var currentFile = this._deps.config.get('currentFile')
var reg = /(.*)(\/).*/
var path = reg.exec(currentFile)
return path ? path[1] : null
@ -154,47 +153,45 @@ class FileManager {
}
fileRemovedEvent (path) {
var self = this
if (!this.openedFiles[path]) return
if (path === self._deps.config.get('currentFile')) {
self._deps.config.set('currentFile', '')
if (path === this._deps.config.get('currentFile')) {
this._deps.config.set('currentFile', '')
}
self._deps.editor.discard(path)
this._deps.editor.discard(path)
delete this.openedFiles[path]
this.events.emit('fileRemoved', path)
this.switchFile()
}
switchFile (file) {
var self = this
const _switchFile = (file) => {
this.saveCurrentFile()
this._deps.config.set('currentFile', file)
this.openedFiles[file] = file
this.fileProviderOf(file).get(file, (error, content) => {
if (error) {
console.log(error)
} else {
if (this.fileProviderOf(file).isReadOnly(file)) {
this._deps.editor.openReadOnly(file, content)
} else {
this._deps.editor.open(file, content)
}
this.events.emit('currentFileChanged', file)
}
})
}
if (file) return _switchFile(file)
else {
var browserProvider = self._deps.filesProviders['browser']
var browserProvider = this._deps.filesProviders['browser']
browserProvider.resolveDirectory('browser', (error, filesTree) => {
if (error) console.error(error)
var fileList = Object.keys(filesTree)
if (fileList.length) {
_switchFile(browserProvider.type + '/' + fileList[0])
} else {
self.events.emit('currentFileChanged')
self._deps.editor.displayEmptyReadOnlySession()
}
})
}
function _switchFile (file) {
self.saveCurrentFile()
self._deps.config.set('currentFile', file)
self.openedFiles[file] = file
self.fileProviderOf(file).get(file, (error, content) => {
if (error) {
console.log(error)
} else {
if (self.fileProviderOf(file).isReadOnly(file)) {
self._deps.editor.openReadOnly(file, content)
} else {
self._deps.editor.open(file, content)
}
self.events.emit('currentFileChanged', file)
this.events.emit('currentFileChanged')
this._deps.editor.displayEmptyReadOnlySession()
}
})
}
@ -243,7 +240,6 @@ class FileManager {
}
syncEditor (path) {
var self = this
var currentFile = this._deps.config.get('currentFile')
if (path !== currentFile) return
@ -251,7 +247,7 @@ class FileManager {
if (provider) {
provider.get(currentFile, (error, content) => {
if (error) console.log(error)
self._deps.editor.setText(content)
this._deps.editor.setText(content)
})
} else {
console.log('cannot save ' + currentFile + '. Does not belong to any explorer')

@ -1,3 +1,5 @@
import { ApiFactory } from 'remix-plugin'
var yo = require('yo-yo')
var modalDialog = require('../ui/modaldialog')
@ -14,13 +16,14 @@ var css = csjs`
}
`
export class RemixdHandle {
export class RemixdHandle extends ApiFactory {
constructor (fileSystemExplorer, locahostProvider) {
super()
this.fileSystemExplorer = fileSystemExplorer
this.locahostProvider = locahostProvider
}
profile () {
get profile () {
return {
name: 'remixd',
methods: [],

@ -17,6 +17,8 @@ var globalRegistry = require('../../global/registry')
var css = require('./styles/file-panel-styles')
import { ApiFactory } from 'remix-plugin'
var canUpload = window.File || window.FileReader || window.FileList || window.Blob
/*
@ -36,285 +38,289 @@ var canUpload = window.File || window.FileReader || window.FileList || window.Bl
- call fileProvider API
*/
function filepanel (localRegistry) {
var self = this
self._components = {}
self._components.registry = localRegistry || globalRegistry
self._deps = {
fileProviders: self._components.registry.get('fileproviders').api,
fileManager: self._components.registry.get('filemanager').api,
config: self._components.registry.get('config').api,
pluginManager: self._components.registry.get('pluginmanager').api
}
var fileExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['browser'])
var fileSystemExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['localhost'])
var swarmExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['swarm'])
var githubExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['github'])
var gistExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['gist'])
var configExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['config'])
var httpExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['http'])
var httpsExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['https'])
self.remixdHandle = new RemixdHandle(fileSystemExplorer, self._deps.fileProviders['localhost'])
// ----------------- editor panel ----------------------
self._compilerMetadata = new CompilerMetadata(
{
fileManager: self._deps.fileManager,
pluginManager: self._deps.pluginManager,
config: self._deps.config
module.exports = class Filepanel extends ApiFactory {
constructor (localRegistry) {
super()
var self = this
self._components = {}
self._components.registry = localRegistry || globalRegistry
self._deps = {
fileProviders: self._components.registry.get('fileproviders').api,
fileManager: self._components.registry.get('filemanager').api,
config: self._components.registry.get('config').api,
pluginManager: self._components.registry.get('pluginmanager').api
}
)
self._compilerMetadata.syncContractMetadata()
self.compilerMetadata = () => { return self._compilerMetadata }
function template () {
return yo`
<div class=${css.container}>
<div class="${css.fileexplorer}">
<div class=${css.menu}>
<span onclick=${createNewFile} class="newFile ${css.newFile}" title="Create New File in the Browser Storage Explorer">
<i class="fa fa-plus-circle"></i>
</span>
${canUpload ? yo`
<span class=${css.uploadFile} title="Add Local file to the Browser Storage Explorer">
<label class="fa fa-folder-open">
<input type="file" onchange=${uploadFile} multiple />
</label>
var fileExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['browser'])
var fileSystemExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['localhost'])
var swarmExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['swarm'])
var githubExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['github'])
var gistExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['gist'])
var configExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['config'])
var httpExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['http'])
var httpsExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['https'])
self.remixdHandle = new RemixdHandle(fileSystemExplorer, self._deps.fileProviders['localhost'])
// ----------------- editor panel ----------------------
self._compilerMetadata = new CompilerMetadata(
{
fileManager: self._deps.fileManager,
pluginManager: self._deps.pluginManager,
config: self._deps.config
}
)
self._compilerMetadata.syncContractMetadata()
self.compilerMetadata = () => { return self._compilerMetadata }
function template () {
return yo`
<div class=${css.container}>
<div class="${css.fileexplorer}">
<div class=${css.menu}>
<span onclick=${createNewFile} class="newFile ${css.newFile}" title="Create New File in the Browser Storage Explorer">
<i class="fa fa-plus-circle"></i>
</span>
` : ''}
<span class="${css.gist}" title="Publish all [browser] explorer files to a github gist" onclick=${() => publishToGist('browser')}>
<i class="fa fa-github"></i>
</span>
<span class="${css.gist}" title="Update the current [gist] explorer" onclick=${() => updateGist()}>
<i class="fa fa-github"></i>
</span>
<span class="${css.copyFiles}" title="Copy all files to another instance of Remix IDE" onclick=${copyFiles}>
<i class="fa fa-files-o" aria-hidden="true"></i>
</span>
</div>
<div>
<div class=${css.treeview}>${fileExplorer.init()}</div>
<div class="configexplorer ${css.treeview}">${configExplorer.init()}</div>
<div class="filesystemexplorer ${css.treeview}">${fileSystemExplorer.init()}</div>
<div class="swarmexplorer ${css.treeview}">${swarmExplorer.init()}</div>
<div class="githubexplorer ${css.treeview}">${githubExplorer.init()}</div>
<div class="gistexplorer ${css.treeview}">${gistExplorer.init()}</div>
<div class="httpexplorer ${css.treeview}">${httpExplorer.init()}</div>
<div class="httpsexplorer ${css.treeview}">${httpsExplorer.init()}</div>
${canUpload ? yo`
<span class=${css.uploadFile} title="Add Local file to the Browser Storage Explorer">
<label class="fa fa-folder-open">
<input type="file" onchange=${uploadFile} multiple />
</label>
</span>
` : ''}
<span class="${css.gist}" title="Publish all [browser] explorer files to a github gist" onclick=${() => publishToGist('browser')}>
<i class="fa fa-github"></i>
</span>
<span class="${css.gist}" title="Update the current [gist] explorer" onclick=${() => updateGist()}>
<i class="fa fa-github"></i>
</span>
<span class="${css.copyFiles}" title="Copy all files to another instance of Remix IDE" onclick=${copyFiles}>
<i class="fa fa-files-o" aria-hidden="true"></i>
</span>
</div>
<div>
<div class=${css.treeview}>${fileExplorer.init()}</div>
<div class="configexplorer ${css.treeview}">${configExplorer.init()}</div>
<div class="filesystemexplorer ${css.treeview}">${fileSystemExplorer.init()}</div>
<div class="swarmexplorer ${css.treeview}">${swarmExplorer.init()}</div>
<div class="githubexplorer ${css.treeview}">${githubExplorer.init()}</div>
<div class="gistexplorer ${css.treeview}">${gistExplorer.init()}</div>
<div class="httpexplorer ${css.treeview}">${httpExplorer.init()}</div>
<div class="httpsexplorer ${css.treeview}">${httpsExplorer.init()}</div>
</div>
</div>
</div>
</div>
`
}
`
}
var event = new EventManager()
self.event = event
var element = template()
fileExplorer.ensureRoot()
configExplorer.ensureRoot()
self._deps.fileProviders['localhost'].event.register('connecting', (event) => {
})
var event = new EventManager()
self.event = event
var element = template()
fileExplorer.ensureRoot()
configExplorer.ensureRoot()
self._deps.fileProviders['localhost'].event.register('connecting', (event) => {
})
self._deps.fileProviders['localhost'].event.register('connected', (event) => {
fileSystemExplorer.show()
})
self._deps.fileProviders['localhost'].event.register('connected', (event) => {
fileSystemExplorer.show()
})
self._deps.fileProviders['localhost'].event.register('errored', (event) => {
fileSystemExplorer.hide()
})
self._deps.fileProviders['localhost'].event.register('errored', (event) => {
fileSystemExplorer.hide()
})
self._deps.fileProviders['localhost'].event.register('closed', (event) => {
fileSystemExplorer.hide()
})
self._deps.fileProviders['localhost'].event.register('closed', (event) => {
fileSystemExplorer.hide()
})
fileExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
fileExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
configExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
configExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
fileSystemExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
fileSystemExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
swarmExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
swarmExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
githubExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
githubExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
gistExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
gistExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
httpExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
httpExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
httpsExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
httpsExplorer.events.register('focus', function (path) {
self._deps.fileManager.switchFile(path)
})
self.render = function render () { return element }
self.render = function render () { return element }
function uploadFile (event) {
// TODO The file explorer is merely a view on the current state of
// the files module. Please ask the user here if they want to overwrite
// a file and then just use `files.add`. The file explorer will
// pick that up via the 'fileAdded' event from the files module.
;[...this.files].forEach((file) => {
var files = fileExplorer.files
function loadFile () {
var fileReader = new FileReader()
fileReader.onload = function (event) {
if (helper.checkSpecialChars(file.name)) {
modalDialogCustom.alert('Special characters are not allowed')
return
}
var success = files.set(name, event.target.result)
if (!success) modalDialogCustom.alert('Failed to create file ' + name)
else self.event.trigger('focus', [name])
}
fileReader.readAsText(file)
}
self.profile = function () {
return {
name: 'fileExplorers',
displayName: 'file explorers',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4=',
description: ' - ',
kind: 'fileexplorer'
var name = files.type + '/' + file.name
files.exists(name, (error, exist) => {
if (error) console.log(error)
if (!exist) {
loadFile()
} else {
modalDialogCustom.confirm(null, `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() })
}
})
})
}
}
function uploadFile (event) {
// TODO The file explorer is merely a view on the current state of
// the files module. Please ask the user here if they want to overwrite
// a file and then just use `files.add`. The file explorer will
// pick that up via the 'fileAdded' event from the files module.
;[...this.files].forEach((file) => {
var files = fileExplorer.files
function loadFile () {
var fileReader = new FileReader()
fileReader.onload = function (event) {
if (helper.checkSpecialChars(file.name)) {
modalDialogCustom.alert('Special characters are not allowed')
return
function createNewFile () {
modalDialogCustom.prompt(null, 'File Name', 'Untitled.sol', (input) => {
helper.createNonClashingName(input, self._deps.fileProviders['browser'], (error, newName) => {
if (error) return modalDialogCustom.alert('Failed to create file ' + newName + ' ' + error)
if (!self._deps.fileProviders['browser'].set(newName, '')) {
modalDialogCustom.alert('Failed to create file ' + newName)
} else {
var file = self._deps.fileProviders['browser'].type + '/' + newName
self._deps.fileManager.switchFile(file)
if (file.includes('_test.sol')) {
self.event.trigger('newTestFileCreated', [file])
}
}
var success = files.set(name, event.target.result)
if (!success) modalDialogCustom.alert('Failed to create file ' + name)
else self.event.trigger('focus', [name])
}
fileReader.readAsText(file)
})
}, null, true)
}
// ------------------ gist publish --------------
function updateGist () {
var gistId = self._deps.fileProviders['gist'].id
if (!gistId) {
tooltip('no gist content is currently loaded.')
} else {
toGist('gist', gistId)
}
}
var name = files.type + '/' + file.name
files.exists(name, (error, exist) => {
if (error) console.log(error)
if (!exist) {
loadFile()
} else {
modalDialogCustom.confirm(null, `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() })
}
function publishToGist (fileProviderName) {
modalDialogCustom.confirm(null, 'Are you very sure you want to publish all your files anonymously as a public gist on github.com?', () => {
toGist(fileProviderName)
})
})
}
}
function createNewFile () {
modalDialogCustom.prompt(null, 'File Name', 'Untitled.sol', (input) => {
helper.createNonClashingName(input, self._deps.fileProviders['browser'], (error, newName) => {
if (error) return modalDialogCustom.alert('Failed to create file ' + newName + ' ' + error)
if (!self._deps.fileProviders['browser'].set(newName, '')) {
modalDialogCustom.alert('Failed to create file ' + newName)
function toGist (fileProviderName, id) {
packageFiles(self._deps.fileProviders[fileProviderName], (error, packaged) => {
if (error) {
console.log(error)
modalDialogCustom.alert('Failed to create gist: ' + error)
} else {
var file = self._deps.fileProviders['browser'].type + '/' + newName
self._deps.fileManager.switchFile(file)
if (file.includes('_test.sol')) {
self.event.trigger('newTestFileCreated', [file])
var tokenAccess = self._deps.config.get('settings/gist-access-token')
if (!tokenAccess) {
modalDialogCustom.alert('Remix requires an access token (which includes gists creation permission). Please go to the settings tab for more information.')
} else {
var description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' + queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&gist='
var gists = new Gists({
token: tokenAccess
})
if (id) {
tooltip('Saving gist (' + id + ') ...')
gists.edit({
description: description,
public: true,
files: packaged,
id: id
}, (error, result) => {
cb(error, result)
})
} else {
tooltip('Creating a new gist ...')
gists.create({
description: description,
public: true,
files: packaged
}, (error, result) => {
cb(error, result)
})
}
}
}
})
}, null, true)
}
// ------------------ gist publish --------------
function updateGist () {
var gistId = self._deps.fileProviders['gist'].id
if (!gistId) {
tooltip('no gist content is currently loaded.')
} else {
toGist('gist', gistId)
}
}
function publishToGist (fileProviderName) {
modalDialogCustom.confirm(null, 'Are you very sure you want to publish all your files anonymously as a public gist on github.com?', () => {
toGist(fileProviderName)
})
}
function toGist (fileProviderName, id) {
packageFiles(self._deps.fileProviders[fileProviderName], (error, packaged) => {
function cb (error, data) {
if (error) {
console.log(error)
modalDialogCustom.alert('Failed to create gist: ' + error)
modalDialogCustom.alert('Failed to manage gist: ' + error)
} else {
var tokenAccess = self._deps.config.get('settings/gist-access-token')
if (!tokenAccess) {
modalDialogCustom.alert('Remix requires an access token (which includes gists creation permission). Please go to the settings tab for more information.')
} else {
var description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' + queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&gist='
var gists = new Gists({
token: tokenAccess
if (data.html_url) {
modalDialogCustom.confirm(null, `The gist is at ${data.html_url}. Would you like to open it in a new window?`, () => {
window.open(data.html_url, '_blank')
})
if (id) {
tooltip('Saving gist (' + id + ') ...')
gists.edit({
description: description,
public: true,
files: packaged,
id: id
}, (error, result) => {
cb(error, result)
})
} else {
tooltip('Creating a new gist ...')
gists.create({
description: description,
public: true,
files: packaged
}, (error, result) => {
cb(error, result)
})
}
} else {
modalDialogCustom.alert(data.message + ' ' + data.documentation_url + ' ' + JSON.stringify(data.errors, null, '\t'))
}
}
})
}
}
// ------------------ copy files --------------
function cb (error, data) {
if (error) {
modalDialogCustom.alert('Failed to manage gist: ' + error)
} else {
if (data.html_url) {
modalDialogCustom.confirm(null, `The gist is at ${data.html_url}. Would you like to open it in a new window?`, () => {
window.open(data.html_url, '_blank')
function copyFiles () {
modalDialogCustom.prompt(null, 'To which other remix-ide instance do you want to copy over all files?', 'https://remix.ethereum.org', (target) => {
doCopy(target)
})
function doCopy (target) {
// package only files from the browser storage.
packageFiles(self._deps.fileProviders['browser'], (error, packaged) => {
if (error) {
console.log(error)
} else {
$('<iframe/>', {
src: target,
style: 'display:none;',
load: function () { this.contentWindow.postMessage(['loadFiles', packaged], '*') }
}).appendTo('body')
}
})
} else {
modalDialogCustom.alert(data.message + ' ' + data.documentation_url + ' ' + JSON.stringify(data.errors, null, '\t'))
}
}
}
// ------------------ copy files --------------
function copyFiles () {
modalDialogCustom.prompt(null, 'To which other remix-ide instance do you want to copy over all files?', 'https://remix.ethereum.org', (target) => {
doCopy(target)
})
function doCopy (target) {
// package only files from the browser storage.
packageFiles(self._deps.fileProviders['browser'], (error, packaged) => {
if (error) {
console.log(error)
} else {
$('<iframe/>', {
src: target,
style: 'display:none;',
load: function () { this.contentWindow.postMessage(['loadFiles', packaged], '*') }
}).appendTo('body')
}
})
get profile () {
return {
name: 'fileExplorers',
displayName: 'file explorers',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4=',
description: ' - ',
kind: 'fileexplorer'
}
}
}
@ -340,4 +346,3 @@ function packageFiles (filesProvider, callback) {
})
}
module.exports = filepanel

@ -52,13 +52,15 @@ export class TabProxy {
appStore.event.on('activate', (name) => {
const { profile } = appStore.getOne(name)
if (profile.prefferedLocation === 'mainPanel') {
this.addTab(name, () => {
this.event.emit('switchApp', name)
}, () => {
this.event.emit('closeApp', name)
this.appManager.deactivateOne(name)
})
if (profile.location === 'mainPanel') {
this.addTab(
name,
() => this.event.emit('switchApp', name),
() => {
this.event.emit('closeApp', name)
this.appManager.deactivateOne(name)
}
)
}
})

@ -3,13 +3,16 @@ var StaticAnalysis = require('../staticanalysis/staticAnalysisView')
var EventManager = require('../../lib/events')
var css = require('./styles/analysis-tab-styles')
class AnalysisTab {
import { ApiFactory } from 'remix-plugin'
class AnalysisTab extends ApiFactory {
constructor (registry) {
super()
this.event = new EventManager()
this.registry = registry
}
profile () {
get profile () {
return {
name: 'solidityStaticAnalysis',
displayName: 'solidity static analysis',

@ -17,9 +17,12 @@ var css = require('./styles/compile-tab-styles')
const CompileTabLogic = require('./compileTab/compileTab.js')
const CompilerContainer = require('./compileTab/compilerContainer.js')
class CompileTab {
import { ApiFactory } from 'remix-plugin'
class CompileTab extends ApiFactory {
constructor (registry) {
super()
this.events = new EventEmitter()
this._view = {
el: null,
@ -56,12 +59,16 @@ class CompileTab {
)
}
activate () {
this.listenToEvents()
this.compilerContainer.activate()
}
deactivate () {
get profile () {
return {
displayName: 'solidity compiler',
name: 'solidity',
methods: ['getCompilationResult'],
events: ['compilationFinished'],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE2LjAuMywgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zOnNrZXRjaD0iaHR0cDovL3d3dy5ib2hlbWlhbmNvZGluZy5jb20vc2tldGNoL25zIgoJIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTMwMHB4IiBoZWlnaHQ9IjEzMDBweCIKCSB2aWV3Qm94PSIwIDAgMTMwMCAxMzAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMzAwIDEzMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8dGl0bGU+VmVjdG9yIDE8L3RpdGxlPgo8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KPGcgaWQ9IlBhZ2UtMSIgc2tldGNoOnR5cGU9Ik1TUGFnZSI+Cgk8ZyBpZD0ic29saWRpdHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQwMi4wMDAwMDAsIDExOC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNMYXllckdyb3VwIj4KCQk8ZyBpZD0iR3JvdXAiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPgoJCQk8cGF0aCBpZD0iU2hhcGUiIG9wYWNpdHk9IjAuNDUiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgICAgIiBkPSJNMzcxLjc3MiwxMzUuMzA4TDI0MS4wNjgsMzY3LjYxSC0yMC4xNThsMTMwLjYxNC0yMzIuMzAyCgkJCQlIMzcxLjc3MiIvPgoJCQk8cGF0aCBpZD0iU2hhcGVfMV8iIG9wYWNpdHk9IjAuNiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0yNDEuMDY4LDM2Ny42MWgyNjEuMzE4TDM3MS43NzIsMTM1LjMwOEgxMTAuNDU2CgkJCQlMMjQxLjA2OCwzNjcuNjF6Ii8+CgkJCTxwYXRoIGlkPSJTaGFwZV8yXyIgb3BhY2l0eT0iMC44IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3ICAgICIgZD0iTTExMC40NTYsNTk5LjgyMkwyNDEuMDY4LDM2Ny42MUwxMTAuNDU2LDEzNS4zMDgKCQkJCUwtMjAuMTU4LDM2Ny42MUwxMTAuNDU2LDU5OS44MjJ6Ii8+CgkJCTxwYXRoIGlkPSJTaGFwZV8zXyIgb3BhY2l0eT0iMC40NSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0xMTEuNzIxLDk0OC4yNzVsMTMwLjcwNC0yMzIuMzAzaDI2MS4zMThMMzczLjAzOCw5NDguMjc1CgkJCQlIMTExLjcyMSIvPgoJCQk8cGF0aCBpZD0iU2hhcGVfNF8iIG9wYWNpdHk9IjAuNiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0yNDIuNDI0LDcxNS45NzNILTE4Ljg5M2wxMzAuNjEzLDIzMi4zMDNoMjYxLjMxNwoJCQkJTDI0Mi40MjQsNzE1Ljk3M3oiLz4KCQkJPHBhdGggaWQ9IlNoYXBlXzVfIiBvcGFjaXR5PSIwLjgiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgICAgIiBkPSJNMzczLjAzOCw0ODMuNzYxTDI0Mi40MjQsNzE1Ljk3M2wxMzAuNjE0LDIzMi4zMDMKCQkJCWwxMzAuNzA0LTIzMi4zMDNMMzczLjAzOCw0ODMuNzYxeiIvPgoJCTwvZz4KCTwvZz4KPC9nPgo8L3N2Zz4K',
description: 'compile solidity contracts',
kind: 'compile'
}
}
/************
@ -131,22 +138,8 @@ class CompileTab {
})
}
profile () {
return {
displayName: 'solidity compiler',
name: 'solidity',
methods: ['getCompilationResult'],
events: ['compilationFinished'],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE2LjAuMywgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zOnNrZXRjaD0iaHR0cDovL3d3dy5ib2hlbWlhbmNvZGluZy5jb20vc2tldGNoL25zIgoJIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTMwMHB4IiBoZWlnaHQ9IjEzMDBweCIKCSB2aWV3Qm94PSIwIDAgMTMwMCAxMzAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMzAwIDEzMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8dGl0bGU+VmVjdG9yIDE8L3RpdGxlPgo8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KPGcgaWQ9IlBhZ2UtMSIgc2tldGNoOnR5cGU9Ik1TUGFnZSI+Cgk8ZyBpZD0ic29saWRpdHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQwMi4wMDAwMDAsIDExOC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNMYXllckdyb3VwIj4KCQk8ZyBpZD0iR3JvdXAiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPgoJCQk8cGF0aCBpZD0iU2hhcGUiIG9wYWNpdHk9IjAuNDUiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgICAgIiBkPSJNMzcxLjc3MiwxMzUuMzA4TDI0MS4wNjgsMzY3LjYxSC0yMC4xNThsMTMwLjYxNC0yMzIuMzAyCgkJCQlIMzcxLjc3MiIvPgoJCQk8cGF0aCBpZD0iU2hhcGVfMV8iIG9wYWNpdHk9IjAuNiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0yNDEuMDY4LDM2Ny42MWgyNjEuMzE4TDM3MS43NzIsMTM1LjMwOEgxMTAuNDU2CgkJCQlMMjQxLjA2OCwzNjcuNjF6Ii8+CgkJCTxwYXRoIGlkPSJTaGFwZV8yXyIgb3BhY2l0eT0iMC44IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3ICAgICIgZD0iTTExMC40NTYsNTk5LjgyMkwyNDEuMDY4LDM2Ny42MUwxMTAuNDU2LDEzNS4zMDgKCQkJCUwtMjAuMTU4LDM2Ny42MUwxMTAuNDU2LDU5OS44MjJ6Ii8+CgkJCTxwYXRoIGlkPSJTaGFwZV8zXyIgb3BhY2l0eT0iMC40NSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0xMTEuNzIxLDk0OC4yNzVsMTMwLjcwNC0yMzIuMzAzaDI2MS4zMThMMzczLjAzOCw5NDguMjc1CgkJCQlIMTExLjcyMSIvPgoJCQk8cGF0aCBpZD0iU2hhcGVfNF8iIG9wYWNpdHk9IjAuNiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0yNDIuNDI0LDcxNS45NzNILTE4Ljg5M2wxMzAuNjEzLDIzMi4zMDNoMjYxLjMxNwoJCQkJTDI0Mi40MjQsNzE1Ljk3M3oiLz4KCQkJPHBhdGggaWQ9IlNoYXBlXzVfIiBvcGFjaXR5PSIwLjgiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgICAgIiBkPSJNMzczLjAzOCw0ODMuNzYxTDI0Mi40MjQsNzE1Ljk3M2wxMzAuNjE0LDIzMi4zMDMKCQkJCWwxMzAuNzA0LTIzMi4zMDNMMzczLjAzOCw0ODMuNzYxeiIvPgoJCTwvZz4KCTwvZz4KPC9nPgo8L3N2Zz4K',
description: 'compile solidity contracts',
kind: 'compile'
}
}
getCompilationResult () {
return new Promise((resolve, reject) => {
resolve(this.compileTabLogic.compiler.lastCompilationResult)
})
return this.compileTabLogic.compiler.lastCompilationResult
}
/*********
@ -340,6 +333,8 @@ class CompileTab {
render () {
if (this._view.el) return this._view.el
this.listenToEvents()
this.compilerContainer.activate()
this._view.errorContainer = yo`<div></div>`
this._view.contractSelection = this.contractSelection()

@ -3,12 +3,16 @@ var css = require('./styles/debugger-tab-styles')
var DebuggerUI = require('../debugger/debuggerUI')
class DebuggerTab {
import { ApiFactory } from 'remix-plugin'
class DebuggerTab extends ApiFactory {
constructor () {
super()
this.el = null
}
profile () {
get profile () {
return {
displayName: 'debugger',
name: 'debugger',

@ -12,9 +12,12 @@ var ContractDropdownUI = require('./runTab/contractDropdown.js')
var Recorder = require('./runTab/model/recorder.js')
var RecorderUI = require('./runTab/recorder.js')
class RunTab {
import { ApiFactory } from 'remix-plugin'
class RunTab extends ApiFactory {
constructor (udapp, udappUI, config, fileManager, editor, logCallback, filePanel, pluginManager, compilersArtefacts) {
super()
this.event = new EventManager()
this.renderInstanceContainer()
@ -25,6 +28,18 @@ class RunTab {
this.renderContainer()
}
get profile () {
return {
name: 'run',
displayName: 'run transactions',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNTc2IDkyN2wtMTMyOCA3MzhxLTIzIDEzLTM5LjUgM3QtMTYuNS0zNnYtMTQ3MnEwLTI2IDE2LjUtMzZ0MzkuNSAzbDEzMjggNzM4cTIzIDEzIDIzIDMxdC0yMyAzMXoiLz48L3N2Zz4=',
description: 'execute and save transactions',
kind: 'run'
}
}
renderContainer () {
this.container = yo`<div class="${css.runTabView}" id="runTabView" ></div>`
@ -147,17 +162,6 @@ class RunTab {
return this.container
}
profile () {
return {
name: 'run',
displayName: 'run transactions',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNTc2IDkyN2wtMTMyOCA3MzhxLTIzIDEzLTM5LjUgM3QtMTYuNS0zNnYtMTQ3MnEwLTI2IDE2LjUtMzZ0MzkuNSAzbDEzMjggNzM4cTIzIDEzIDIzIDMxdC0yMyAzMXoiLz48L3N2Zz4=',
description: 'execute and save transactions',
kind: 'run'
}
}
}
module.exports = RunTab

@ -9,8 +9,11 @@ var styleGuide = require('../ui/styles-guide/theme-chooser')
var Storage = remixLib.Storage
var EventManager = require('../../lib/events')
module.exports = class SettingsTab {
import { ApiFactory } from 'remix-plugin'
module.exports = class SettingsTab extends ApiFactory {
constructor (localRegistry) {
super()
const self = this
self._components = {}
self._components.registry = localRegistry || globalRegistry
@ -37,7 +40,7 @@ module.exports = class SettingsTab {
self._components.themeStorage = new Storage('style:')
self.data.currentTheme = self._components.themeStorage.get('theme') || 'light'
}
profile () {
get profile () {
return {
displayName: 'settings',
name: 'settings',
@ -45,9 +48,11 @@ module.exports = class SettingsTab {
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xMTUyIDg5NnEwLTEwNi03NS0xODF0LTE4MS03NS0xODEgNzUtNzUgMTgxIDc1IDE4MSAxODEgNzUgMTgxLTc1IDc1LTE4MXptNTEyLTEwOXYyMjJxMCAxMi04IDIzdC0yMCAxM2wtMTg1IDI4cS0xOSA1NC0zOSA5MSAzNSA1MCAxMDcgMTM4IDEwIDEyIDEwIDI1dC05IDIzcS0yNyAzNy05OSAxMDh0LTk0IDcxcS0xMiAwLTI2LTlsLTEzOC0xMDhxLTQ0IDIzLTkxIDM4LTE2IDEzNi0yOSAxODYtNyAyOC0zNiAyOGgtMjIycS0xNCAwLTI0LjUtOC41dC0xMS41LTIxLjVsLTI4LTE4NHEtNDktMTYtOTAtMzdsLTE0MSAxMDdxLTEwIDktMjUgOS0xNCAwLTI1LTExLTEyNi0xMTQtMTY1LTE2OC03LTEwLTctMjMgMC0xMiA4LTIzIDE1LTIxIDUxLTY2LjV0NTQtNzAuNXEtMjctNTAtNDEtOTlsLTE4My0yN3EtMTMtMi0yMS0xMi41dC04LTIzLjV2LTIyMnEwLTEyIDgtMjN0MTktMTNsMTg2LTI4cTE0LTQ2IDM5LTkyLTQwLTU3LTEwNy0xMzgtMTAtMTItMTAtMjQgMC0xMCA5LTIzIDI2LTM2IDk4LjUtMTA3LjV0OTQuNS03MS41cTEzIDAgMjYgMTBsMTM4IDEwN3E0NC0yMyA5MS0zOCAxNi0xMzYgMjktMTg2IDctMjggMzYtMjhoMjIycTE0IDAgMjQuNSA4LjV0MTEuNSAyMS41bDI4IDE4NHE0OSAxNiA5MCAzN2wxNDItMTA3cTktOSAyNC05IDEzIDAgMjUgMTAgMTI5IDExOSAxNjUgMTcwIDcgOCA3IDIyIDAgMTItOCAyMy0xNSAyMS01MSA2Ni41dC01NCA3MC41cTI2IDUwIDQxIDk4bDE4MyAyOHExMyAyIDIxIDEyLjV0OCAyMy41eiIvPjwvc3ZnPg==',
description: ' - ',
kind: 'settings'
kind: 'settings',
location: 'swapPanel'
}
}
render () {
const self = this
if (self._view.el) return self._view.el

@ -1,9 +1,12 @@
const yo = require('yo-yo')
var css = require('./styles/support-tab-styles')
class SupportTab {
import { ApiFactory } from 'remix-plugin'
class SupportTab extends ApiFactory {
constructor (localRegistry) {
super()
this.el = null
this.gitterIframe = ''
this.gitterIsLoaded = false
@ -18,7 +21,8 @@ class SupportTab {
this.el.style.display = 'block'
this.gitterIsLoaded = true
}
profile () {
get profile () {
return {
name: 'support',
methods: [],
@ -27,6 +31,7 @@ class SupportTab {
description: 'help center'
}
}
render () {
if (this.el) return this.el

@ -7,8 +7,11 @@ var globalRegistry = require('../../global/registry')
var css = require('./styles/test-tab-styles')
var remixTests = require('remix-tests')
module.exports = class TestTab {
import { ApiFactory } from 'remix-plugin'
module.exports = class TestTab extends ApiFactory {
constructor (localRegistry, compileTab) {
super()
// TODO here is a direct reference to compile tab, should be removed
const self = this
self.compileTab = compileTab
@ -23,7 +26,8 @@ module.exports = class TestTab {
self.data = {}
self.testList = yo`<div class=${css.testList}></div>`
}
profile () {
get profile () {
return {
name: 'solidityUnitTesting',
displayName: 'solidity unit testing',
@ -33,6 +37,7 @@ module.exports = class TestTab {
description: ' - '
}
}
render () {
const self = this
var testsOutput = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`

@ -0,0 +1,22 @@
import { ApiFactory } from 'remix-plugin'
import { EventEmitter } from 'events'
export class TxListenerModule extends ApiFactory {
constructor (txlistener) {
super()
this.events = new EventEmitter()
txlistener.event.register('newTransaction', (tx) => {
this.events.emit('newTransaction', tx)
})
}
get profile () {
return {
name: 'txListener',
displayName: 'transaction listener',
events: ['newTransaction'],
description: 'service - notify new transactions'
}
}
}

@ -1,73 +0,0 @@
/* global */
import LandingPage from './landing-page'
import Section from './section'
import { defaultWorkspaces } from './workspace'
export function homepageProfile () {
return {
displayName: 'home',
name: 'home',
methods: [],
events: [],
description: ' - ',
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxnPjxwYXRoIGQ9IiAgIE0yNSwxQzExLjc0NSwxLDEsMTEuNzQ1LDEsMjVzMTAuNzQ1LDI0LDI0LDI0czI0LTEwLjc0NSwyNC0yNFMzOC4yNTUsMSwyNSwxTDI1LDF6IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiLz48L2c+PHBhdGggZD0iICBNNDAuNjk2LDYuODMyYzAsMC0xMy4xNjksOC4yMTItMTEuNTMyLDIyLjMzMmMxLjE0Miw5Ljg1OCwxMS45MzUsMTMuMzc3LDExLjkzNSwxMy4zNzciIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMi4wNTgzIi8+PHBhdGggZD0iICBNNy4zODUsOC45MTNjMCwwLDMuMDQxLDYuNDc2LDMuMDQxLDE4LjE2OWMwLDkuMjQ2LTMuNTgzLDEyLjkxMS0zLjU4MywxMi45MTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMi4wNTgzIi8+PHBhdGggZD0iICBNMS44NTIsMjIuOTMyYzAsMCw2LjQ5Myw2LjIzMiwyMy4xNDgsNi4yMzJzMjMuNDM4LTYuMjQ2LDIzLjQzOC02LjI0NiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyLjA1ODMiLz48cGF0aCBkPSIgIE0yNS42NDgsMS41NDhjMCwwLTYuODk1LDcuOTM1LTYuODk1LDIzLjQ1MkMxOC43NTQsNDAuNTE4LDI1LDQ4LjYyNSwyNSw0OC42MjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMi4wNTgzIi8+PC9zdmc+',
prefferedLocation: 'mainPanel'
}
}
export function generateHomePage (appManager, appStore) {
/*
var actions1 = [
{label: 'new file', type: `callback`, payload: () => { alert(`-new file created-`) }},
{label: 'import from GitHub', type: `callback`, payload: () => { alert(`-imported from GitHub-`) }},
{label: 'import from gist', type: `callback`, payload: () => { alert(`-imported from gist-`) }}
]
var actions2 = [
{label: '...', type: `callback`, payload: () => { alert(`-...-`) }}
]
var actions3 = [
{label: 'Remix documentation', type: `link`, payload: `https://remix.readthedocs.io/en/latest/#`},
{label: 'GitHub repository', type: `link`, payload: `https://github.com/ethereum/remix-ide`},
{label: 'acces local file system (remixd)', type: `link`, payload: `https://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html`},
{label: 'npm module for remixd', type: `link`, payload: `https://www.npmjs.com/package/remixd`},
{label: 'medium posts', type: `link`, payload: `https://medium.com/remix-ide`},
{label: 'tutorials', type: `link`, payload: `https://github.com/ethereum/remix-workshops`}
]
var actions4 = [
{label: 'Remix plugins & modules', type: `link`, payload: `https://github.com/ethereum/remix-plugin/blob/master/readme.md`},
{label: 'repository on GitHub', type: `link`, payload: `https://github.com/ethereum/remix-plugin`},
{label: 'examples', type: `link`, payload: `https://github.com/ethereum/remix-plugin/tree/master/examples`},
{label: 'build plugin for Remix', type: `link`, payload: `https://medium.com/remix-ide/build-a-plugin-for-remix-90d43b209c5a`}
]
var actions5 = [
{label: 'Gitter channel', type: `link`, payload: `https://gitter.im/ethereum/remix`},
{label: 'Stack Overflow', type: `link`, payload: `https://stackoverflow.com/questions/tagged/remix`},
{label: 'Reddit', type: `link`, payload: `https://www.reddit.com/r/ethdev/search?q=remix&restrict_sr=1`}
]
var section1 = new Section('Start', actions1)
var section2 = new Section('Recent', actions2)
var section3 = new Section('Learn', actions3)
var section4 = new Section('Plugins', actions4)
var section5 = new Section('Help', actions5)
*/
var sectionsWorkspaces = []
sectionsWorkspaces.push({
label: 'Close All Modules',
type: 'callback',
payload: () => {
appStore.getActives()
.filter(({profile}) => !profile.required)
.forEach((profile) => { appManager.deactivateOne(profile.name) })
}})
defaultWorkspaces(appManager).forEach((workspace) => {
sectionsWorkspaces.push({label: workspace.title, type: 'callback', payload: () => { workspace.activate() }})
})
var sectionWorkspace = new Section('Workspaces', sectionsWorkspaces)
return new LandingPage([sectionWorkspace])
}

@ -27,9 +27,79 @@ var css = csjs`
}
`
class LandingPage {
constructor (sections) {
this.sections = sections
import { defaultWorkspaces } from './workspace'
import { ApiFactory } from 'remix-plugin'
import Section from './section'
export class LandingPage extends ApiFactory {
constructor (appManager, appStore) {
super()
/*
var actions1 = [
{label: 'new file', type: `callback`, payload: () => { alert(`-new file created-`) }},
{label: 'import from GitHub', type: `callback`, payload: () => { alert(`-imported from GitHub-`) }},
{label: 'import from gist', type: `callback`, payload: () => { alert(`-imported from gist-`) }}
]
var actions2 = [
{label: '...', type: `callback`, payload: () => { alert(`-...-`) }}
]
var actions3 = [
{label: 'Remix documentation', type: `link`, payload: `https://remix.readthedocs.io/en/latest/#`},
{label: 'GitHub repository', type: `link`, payload: `https://github.com/ethereum/remix-ide`},
{label: 'acces local file system (remixd)', type: `link`, payload: `https://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html`},
{label: 'npm module for remixd', type: `link`, payload: `https://www.npmjs.com/package/remixd`},
{label: 'medium posts', type: `link`, payload: `https://medium.com/remix-ide`},
{label: 'tutorials', type: `link`, payload: `https://github.com/ethereum/remix-workshops`}
]
var actions4 = [
{label: 'Remix plugins & modules', type: `link`, payload: `https://github.com/ethereum/remix-plugin/blob/master/readme.md`},
{label: 'repository on GitHub', type: `link`, payload: `https://github.com/ethereum/remix-plugin`},
{label: 'examples', type: `link`, payload: `https://github.com/ethereum/remix-plugin/tree/master/examples`},
{label: 'build plugin for Remix', type: `link`, payload: `https://medium.com/remix-ide/build-a-plugin-for-remix-90d43b209c5a`}
]
var actions5 = [
{label: 'Gitter channel', type: `link`, payload: `https://gitter.im/ethereum/remix`},
{label: 'Stack Overflow', type: `link`, payload: `https://stackoverflow.com/questions/tagged/remix`},
{label: 'Reddit', type: `link`, payload: `https://www.reddit.com/r/ethdev/search?q=remix&restrict_sr=1`}
]
var section1 = new Section('Start', actions1)
var section2 = new Section('Recent', actions2)
var section3 = new Section('Learn', actions3)
var section4 = new Section('Plugins', actions4)
var section5 = new Section('Help', actions5)
*/
const sectionsWorkspaces = []
sectionsWorkspaces.push({
label: 'Close All Modules',
type: 'callback',
payload: () => {
appStore.getActives()
.filter(({profile}) => !profile.required)
.forEach((profile) => { appManager.deactivateOne(profile.name) })
}})
defaultWorkspaces(appManager).forEach((workspace) => {
sectionsWorkspaces.push({label: workspace.title, type: 'callback', payload: () => { workspace.activate() }})
})
const sectionWorkspace = new Section('Workspaces', sectionsWorkspaces)
this.sections = sectionWorkspace
}
get profile () {
return {
displayName: 'home',
name: 'home',
methods: [],
events: [],
description: ' - ',
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxnPjxwYXRoIGQ9IiAgIE0yNSwxQzExLjc0NSwxLDEsMTEuNzQ1LDEsMjVzMTAuNzQ1LDI0LDI0LDI0czI0LTEwLjc0NSwyNC0yNFMzOC4yNTUsMSwyNSwxTDI1LDF6IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiLz48L2c+PHBhdGggZD0iICBNNDAuNjk2LDYuODMyYzAsMC0xMy4xNjksOC4yMTItMTEuNTMyLDIyLjMzMmMxLjE0Miw5Ljg1OCwxMS45MzUsMTMuMzc3LDExLjkzNSwxMy4zNzciIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMi4wNTgzIi8+PHBhdGggZD0iICBNNy4zODUsOC45MTNjMCwwLDMuMDQxLDYuNDc2LDMuMDQxLDE4LjE2OWMwLDkuMjQ2LTMuNTgzLDEyLjkxMS0zLjU4MywxMi45MTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMi4wNTgzIi8+PHBhdGggZD0iICBNMS44NTIsMjIuOTMyYzAsMCw2LjQ5Myw2LjIzMiwyMy4xNDgsNi4yMzJzMjMuNDM4LTYuMjQ2LDIzLjQzOC02LjI0NiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyLjA1ODMiLz48cGF0aCBkPSIgIE0yNS42NDgsMS41NDhjMCwwLTYuODk1LDcuOTM1LTYuODk1LDIzLjQ1MkMxOC43NTQsNDAuNTE4LDI1LDQ4LjYyNSwyNSw0OC42MjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMi4wNTgzIi8+PC9zdmc+',
location: 'mainPanel'
}
}
render () {
@ -48,5 +118,3 @@ class LandingPage {
return totalLook
}
}
module.exports = LandingPage

@ -1,13 +1,10 @@
export default {
start: (appStore, swapPanelApi, verticalIconApi, mainPanelApi, resizeFeature) => {
swapPanelApi.event.on('toggle', (moduleName) => {
swapPanelApi.event.on('toggle', () => {
resizeFeature.panel1.clientWidth !== 0 ? resizeFeature.minimize() : resizeFeature.maximise()
})
swapPanelApi.event.on('showing', (moduleName) => {
swapPanelApi.event.on('showing', () => {
resizeFeature.panel1.clientWidth === 0 ? resizeFeature.maximise() : ''
var current = appStore.getOne(moduleName)
// warn the content that it is being displayed. TODO should probably be done in each view
if (current && current.api.__showing) current.api.__showing()
})
mainPanelApi.event.on('toggle', () => {
resizeFeature.maximise()

@ -135,7 +135,8 @@ export class EntityStore extends Store {
* Add a new entity to the state
* @param {Object} entity
*/
add (id, entity) {
add (entity) {
const id = entity[this.keyId]
this.state.entities[id] = entity
this.state.ids.push(id)
this.event.emit('add', entity)

@ -14,8 +14,8 @@ export class RemixAppManager extends AppManagerApi {
}
}
ensureActivated (module) {
if (!this.store.isActive(module)) this.activateOne(module)
ensureActivated (apiName) {
if (!this.store.isActive(apiName)) this.activateOne(apiName)
}
proxy () {
@ -24,33 +24,23 @@ export class RemixAppManager extends AppManagerApi {
}
setActive (name, isActive) {
const entity = this.getEntity(name)
const api = this.getEntity(name)
// temp
if (entity && (name === 'solidity' || name === 'vyper')) {
isActive ? this.data.proxy.register(name, entity.api) : this.data.proxy.unregister(name, entity.api)
if (api && (name === 'solidity' || name === 'vyper')) {
isActive ? this.data.proxy.register(name, api) : this.data.proxy.unregister(name, api)
}
isActive ? this.store.activate(name) : this.store.deactivate(name)
if (!isActive) {
this.removeHiddenServices(entity)
this.removeHiddenServices(api)
}
}
getEntity (entityName) {
return this.store.getOne(entityName)
getEntity (apiName) {
return this.store.getOne(apiName)
}
addEntity (entity) {
this.store.add(entity.profile.name, entity)
}
// this function is only used for iframe plugins
resolveLocation (profile, domEl) {
if (profile.icon) {
this.event.emit('pluginNeedsLocation', profile, domEl)
} else {
this.hiddenServices[profile.name] = domEl
document.body.appendChild(domEl)
}
addEntity (api) {
this.store.add(api)
}
removeHiddenServices (profile) {
@ -81,10 +71,11 @@ export class RemixAppManager extends AppManagerApi {
url: 'https://pipeline-alpha.pipeos.one',
description: 'Visual IDE for contracts and dapps',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAAAAACIM/FCAAAJU0lEQVR42u3ce1QUVRgA8EEemgoWvjkltIZlIJhCmmTmqVBJJSofaaWpYUUn08wHmkfR6GFWqKh51CDzLZkPKs2MNAkFVAgxw007WZklaWqsLutt0b13LusM7LDfN+x07vcXe3dmvvkxszN35rszEvmfhCQgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgHgspXbcsdVoybKQsXrPfqivk0qoBJqSImV+uG6Tyq34mxOi+7oI+EMt4E3L0P60H5PRjJvSIOYwPsejgsO9ev6FDXjXpEgMvIUM2mXSKibgQWz+9IJ3OoEJWcbtx+t4yM2gcK8jk/k9TUSHyefClUjNClM0NpQnCLiJCfmCOJDNSLGQpNiJCVgQ1b+YfcFPrdt1KsSDmwSFtAgP8AwLbjMeCVGYOu1FyRPvEfBzG4WlRXo4cfvGv/4sByYmQ+Gg8/hiC4/1W1ZIEZbgNWb62+ueKV/0kp+i2FZqR19/LOcnQ392EJEpdc7iP1u7S9XHDZlhH4a0KSW7+xV2IJD28hW2PxyWlaLEF0pEbrpgk6qTbEEnq8uW1TyMl5QiE/MmHqyTpesl9iH2rVB3NCyS1GAHnmKua5C0IiH2rZFdGqOaQ1kM58puo5vA/DQKxb1v5zzZPb8zb+XpnuSEGCjJWXqZpXPa+7EkmuWEyEESO4Y4z1AYf1vQpEOQGtsQZtmsd7VT5nHW+zpATCUqO/uxnt9qXts2EcexiSeRe71TWllU3iC2zZwMlxy1cjyGZNibAQF6jy+vDrUgf2jivTpC9d6n86DrlyROdpo0dYSCD6PKWc2vyFW0cXQdI5cvqhygp7JMrdLrmjqYmMJBuNMXxaqvniPvrABki1Rg9yhzTRdIW4LOhVQkSpR0ytWZH30I6IT06+sJA2O5crgS5RzNkd4OaGJE75SnphK1hIA/SHFwK8hdtHKgVUtG+Jke0TZ5yK228DwbyAl3eFG51ZtLGaVohu2vesbayCS+E0rZxMJBldHlNyliSMtZr+UArZFTNEP9sx3R/RrG2dBhIqXwBUuRIcvRm1nZEI8TWku2UwxUlvslV/bfKTWx7SAElQF2ULmyRzdOrTryW9ObyoVJrX+t7OmfTP7i+Vv+PmsofmsSOGsxfyE2E6jRm8pc5A0cP5C/et2uFfEznfEruNEblEpKiurO1OwIFkY9b10W85uuR2XI/wQF5ZFvVkcpiUsuxEsxhzm2kkqNxkWbIeDrvrmuQe3IdXxxQueoZYwaM91Qgq4lmyHN03n1VkDi5Y0W+aKiUYuiPkBBzip9SkneIdgj7YRSQxN77qp9hWihsD1iH/bLd97ocfiuIe5ATzlOefNT5d77EDB6bna8guhUSNyEK8W0MlyHgtSNmhDi2gD+uBK+/QhAghOQ/3NDXS5K8/Rr3KjEjxY+jmvrZk3j5+EVuryQEB0J2seLFKizInjtpivmEoEEudqJZInbjOArj2f+qBBFCZrI0HaYj1HrKFnRmCR4nmJDjoXK1MmxQ0ouw8WQXrqz7GSqEvK1XeTqe4EJ+7ayPo30uMoQcDtMFsplgQ0iWHo6ZBB9CcqKwGbevrNQDQn7ui+u4O4cQXSDk8sYBeIyeS88RvSD2KJ7RB0UxYWclIXpCqvorP31fDBtlZ0ldwx2IR8X/FMIKkvsNDmE1nhyDQ9jd7wyDQz6kkLEGhxSxO7x/GRtibUYlE/4xNISrj7Sds99QFifIF5IHh1ejkIcmbbjkEsQS7MkSx83HPBcgZI3nQySv0Rdqh5DpBpBIwUdqh9juMoKk9alaIeTcy0aQ3Hu5Vgghac0MIHnDBQgp/6hHA0+HND7uAsQeJz4eFx8T1dWjIiKEL80tdA3imWH9ZoI3hfQxMsQeKynE94KxIYSNX8oxOGQyhSwxOGQ9hUw3OGQPhbxkcEg+hSQaHFIgIMpR/u2m1bCxZsv+8zpDbCULHkKpKIQmrPxJR0jpMMwqT9IpvSAZobj1Kn6UNCLk7BD8SugUKz7E9rweNd0UfEiSLkV202JsyAZ9HKbQIlxIRS+dIKancCEZXKrBs9Lmg0Zachy3+AJMiLUnyzPmM4xRZxvk8v2zmJA9LM0InOFz5h9iaYbbziBCUoPbtmzRsm2wqfchrBGNOeGmdm1atmgVFJKFBvklNYRe9yfsxYJ8Hk2fOe245CwK5NAr/Bj8hmOyMRirHvHmkgTOOQkPyfBxutPn/TY442iSt1OSZruBIValR5YGHYV1HIi5PofPHFCI7QnF268DQN+4UxSmmGSu88rk0W9e0A6ZpHIjeSwkpLdyDi+nt4HIZc6JmiE7VW+JAz7Pk6qWw6es+trMo1/M0wqxdFWFhIM9sZCnXpMZWX11Yml7llbIfG6hvVIWpfTiPs+AgnAPp3jHv7VoaiSXZC+/Nj+z5t+0QjqwWSOvFYbz5DLjrUCv3cmXKx+xV28/XMkKYi1DuZUp6UhbOxCNkINsgXG0Vm+JY21Ab3hho3nk91T8fTvbRAs/uBoLU0fKD3JKs7RC1tI5g+QHtMvZvysNBvIsXd598tDMQz411t52aIUk0TnXKumehIF0psv7zoWj/tWIJ1oh/eisxVxjMfv1w0DoY7l+/OvaanqUu+kxzZBoOm8F11hBGyNgILSIHKbYGVGIbaTuEAt/bqGNnWAg9LVa4S5C3iTaIX3pzPwjTyXAu1agY3GNXNq1AneQOkA878c+Ruk9p7VDWD24A3f4ZSfJd2Egz7BzlXz4LQ5QQPjHpim/eFbTCdFSvyfEpZnZJTa11awdcll+5rS3o4vCjshSO6CLqzy5izLE0UVhBxkpwZVLDa2dxlmLZunQaUxX7zS6Afn3TtWj4G2HoSC71Lvxrj1YabwLKzcgKm8/s/evzYARrXKpu5jAQWzD9Lj5EO7azQd3IMQap5DifuAXJRy4Q2G/SiKgEE+6QecmhBwcUO2W6QPbzAjxYTRPCXjmJIGH1MNNbE2PI2oqK6TQssKDR7Eg+ayssI4QNIhHFHogINZoJkk8iOHYI5feniaIEI8ohoJAKmL0Kk8PJ6gQ/QYMFCJDPGAIBxDk4mg9HBMJOqT+hzlBQcjl97AHnq0hRA9IfQ8FBIQQ29fJWIMzZxcRoh+kKuptuCw0xNNCQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQATEY+M/DV/rjLxphEwAAAAASUVORK5CYII=',
prefferedLocation: 'mainPanel'
location: 'mainPanel'
}
const plugins = [{ profile: pipeline, api: new Plugin(pipeline, { resolveLocaton: (iframe) => { return this.resolveLocation(pipeline, iframe) } }) },
{ profile: vyper, api: new Plugin(vyper, { resolveLocaton: (iframe) => { return this.resolveLocation(vyper, iframe) } }) }]
return plugins
return [
new Plugin(pipeline),
new Plugin(vyper)
]
}
}

@ -7,349 +7,345 @@ var TxRunner = remixLib.execution.txRunner
var txHelper = remixLib.execution.txHelper
var EventManager = remixLib.EventManager
var executionContext = remixLib.execution.executionContext
import { ApiFactory } from 'remix-plugin'
function UniversalDApp (registry) {
this.event = new EventManager()
var self = this
self._deps = {
config: registry.get('config').api
}
self._txRunnerAPI = {
config: self._deps.config,
detectNetwork: (cb) => {
executionContext.detectNetwork(cb)
},
personalMode: () => {
return self._deps.config.get('settings/personal-mode')
module.exports = class UniversalDApp extends ApiFactory {
constructor (registry) {
super()
this.event = new EventManager()
this._deps = {
config: registry.get('config').api
}
this._txRunnerAPI = {
config: this._deps.config,
detectNetwork: (cb) => {
executionContext.detectNetwork(cb)
},
personalMode: () => {
return this._deps.config.get('settings/personal-mode')
}
}
this.txRunner = new TxRunner({}, this._txRunnerAPI)
this.accounts = {}
this.resetEnvironment()
executionContext.event.register('contextChanged', this.resetEnvironment.bind(this))
}
self.txRunner = new TxRunner({}, self._txRunnerAPI)
self.accounts = {}
self.resetEnvironment()
executionContext.event.register('contextChanged', this.resetEnvironment.bind(this))
}
UniversalDApp.prototype.profile = function () {
return {
name: 'udapp',
displayName: 'universal dapp',
methods: ['runTestTx', 'getAccounts', 'createVMAccount'],
description: 'service - run transaction and access account'
get profile () {
return {
name: 'udapp',
displayName: 'universal dapp',
methods: ['runTestTx', 'getAccounts', 'createVMAccount'],
description: 'service - run transaction and access account'
}
}
}
UniversalDApp.prototype.resetEnvironment = function () {
this.accounts = {}
if (executionContext.isVM()) {
this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511', '0x56BC75E2D63100000')
this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c', '0x56BC75E2D63100000')
this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', '0x56BC75E2D63100000')
this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea', '0x56BC75E2D63100000')
this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000')
executionContext.vm().stateManager.cache.flush(function () {})
}
// TODO: most params here can be refactored away in txRunner
this.txRunner = new TxRunner(this.accounts, {
// TODO: only used to check value of doNotShowTransactionConfirmationAgain property
config: this.config,
// TODO: to refactor, TxRunner already has access to executionContext
detectNetwork: (cb) => {
executionContext.detectNetwork(cb)
},
personalMode: () => {
return this.config.get('settings/personal-mode')
resetEnvironment () {
this.accounts = {}
if (executionContext.isVM()) {
this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511', '0x56BC75E2D63100000')
this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c', '0x56BC75E2D63100000')
this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', '0x56BC75E2D63100000')
this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea', '0x56BC75E2D63100000')
this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000')
executionContext.vm().stateManager.cache.flush(function () {})
}
})
this.txRunner.event.register('transactionBroadcasted', (txhash) => {
executionContext.detectNetwork((error, network) => {
if (error || !network) return
this.event.trigger('transactionBroadcasted', [txhash, network.name])
// TODO: most params here can be refactored away in txRunner
this.txRunner = new TxRunner(this.accounts, {
// TODO: only used to check value of doNotShowTransactionConfirmationAgain property
config: this.config,
// TODO: to refactor, TxRunner already has access to executionContext
detectNetwork: (cb) => {
executionContext.detectNetwork(cb)
},
personalMode: () => {
return this.config.get('settings/personal-mode')
}
})
})
}
UniversalDApp.prototype.resetAPI = function (transactionContextAPI) {
this.transactionContextAPI = transactionContextAPI
}
UniversalDApp.prototype.createVMAccount = function (privateKey, balance, cb) {
return new Promise((resolve, reject) => {
if (executionContext.getProvider() !== 'vm') return reject('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed')
this._addAccount(privateKey, balance)
executionContext.vm().stateManager.cache.flush(function () {})
privateKey = Buffer.from(privateKey, 'hex')
resolve('0x' + ethJSUtil.privateToAddress(privateKey).toString('hex'))
})
}
UniversalDApp.prototype.newAccount = function (password, passwordPromptCb, cb) {
if (!executionContext.isVM()) {
if (!this.config.get('settings/personal-mode')) {
return cb('Not running in personal mode')
}
passwordPromptCb((passphrase) => {
executionContext.web3().personal.newAccount(passphrase, cb)
this.txRunner.event.register('transactionBroadcasted', (txhash) => {
executionContext.detectNetwork((error, network) => {
if (error || !network) return
this.event.trigger('transactionBroadcasted', [txhash, network.name])
})
})
} else {
var privateKey
do {
privateKey = crypto.randomBytes(32)
} while (!ethJSUtil.isValidPrivate(privateKey))
this._addAccount(privateKey, '0x56BC75E2D63100000')
executionContext.vm().stateManager.cache.flush(function () {})
cb(null, '0x' + ethJSUtil.privateToAddress(privateKey).toString('hex'))
}
}
UniversalDApp.prototype._addAccount = function (privateKey, balance) {
var self = this
if (!executionContext.isVM()) {
throw new Error('_addAccount() cannot be called in non-VM mode')
resetAPI (transactionContextAPI) {
this.transactionContextAPI = transactionContextAPI
}
if (self.accounts) {
privateKey = Buffer.from(privateKey, 'hex')
var address = ethJSUtil.privateToAddress(privateKey)
// FIXME: we don't care about the callback, but we should still make this proper
executionContext.vm().stateManager.putAccountBalance(address, balance || '0xf00000000000000001', function cb () {})
self.accounts['0x' + address.toString('hex')] = { privateKey: privateKey, nonce: 0 }
createVMAccount (privateKey, balance, cb) {
return new Promise((resolve, reject) => {
if (executionContext.getProvider() !== 'vm') return reject('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed')
this._addAccount(privateKey, balance)
executionContext.vm().stateManager.cache.flush(function () {})
privateKey = Buffer.from(privateKey, 'hex')
resolve('0x' + ethJSUtil.privateToAddress(privateKey).toString('hex'))
})
}
}
// TODO should remove this cb
UniversalDApp.prototype.getAccounts = function (cb) {
var self = this
return new Promise((resolve, reject) => {
newAccount (password, passwordPromptCb, cb) {
if (!executionContext.isVM()) {
// Weirdness of web3: listAccounts() is sync, `getListAccounts()` is async
// See: https://github.com/ethereum/web3.js/issues/442
if (this._deps.config.get('settings/personal-mode')) {
return executionContext.web3().personal.getListAccounts((error, accounts) => {
if (cb) cb(error, accounts)
if (error) return reject(error)
resolve(accounts)
})
} else {
executionContext.web3().eth.getAccounts((error, accounts) => {
if (cb) cb(error, accounts)
if (error) return reject(error)
resolve(accounts)
})
if (!this.config.get('settings/personal-mode')) {
return cb('Not running in personal mode')
}
passwordPromptCb((passphrase) => {
executionContext.web3().personal.newAccount(passphrase, cb)
})
} else {
if (!self.accounts) {
if (cb) cb('No accounts?')
reject('No accounts?')
return
}
if (cb) cb(null, Object.keys(self.accounts))
resolve(Object.keys(self.accounts))
var privateKey
do {
privateKey = crypto.randomBytes(32)
} while (!ethJSUtil.isValidPrivate(privateKey))
this._addAccount(privateKey, '0x56BC75E2D63100000')
executionContext.vm().stateManager.cache.flush(function () {})
cb(null, '0x' + ethJSUtil.privateToAddress(privateKey).toString('hex'))
}
})
}
}
UniversalDApp.prototype.getBalance = function (address, cb) {
var self = this
_addAccount (privateKey, balance) {
if (!executionContext.isVM()) {
throw new Error('_addAccount() cannot be called in non-VM mode')
}
address = ethJSUtil.stripHexPrefix(address)
if (this.accounts) {
privateKey = Buffer.from(privateKey, 'hex')
const address = ethJSUtil.privateToAddress(privateKey)
if (!executionContext.isVM()) {
executionContext.web3().eth.getBalance(address, function (err, res) {
if (err) {
cb(err)
} else {
cb(null, res.toString(10))
}
})
} else {
if (!self.accounts) {
return cb('No accounts?')
// FIXME: we don't care about the callback, but we should still make this proper
executionContext.vm().stateManager.putAccountBalance(address, balance || '0xf00000000000000001', function cb () {})
this.accounts['0x' + address.toString('hex')] = { privateKey, nonce: 0 }
}
}
executionContext.vm().stateManager.getAccountBalance(Buffer.from(address, 'hex'), function (err, res) {
if (err) {
cb('Account not found')
getAccounts (cb) {
return new Promise((resolve, reject) => {
if (!executionContext.isVM()) {
// Weirdness of web3: listAccounts() is sync, `getListAccounts()` is async
// See: https://github.com/ethereum/web3.js/issues/442
if (this._deps.config.get('settings/personal-mode')) {
return executionContext.web3().personal.getListAccounts((error, accounts) => {
if (cb) cb(error, accounts)
if (error) return reject(error)
resolve(accounts)
})
} else {
executionContext.web3().eth.getAccounts((error, accounts) => {
if (cb) cb(error, accounts)
if (error) return reject(error)
resolve(accounts)
})
}
} else {
cb(null, new BN(res).toString(10))
if (!this.accounts) {
if (cb) cb('No accounts?')
reject('No accounts?')
return
}
if (cb) cb(null, Object.keys(this.accounts))
resolve(Object.keys(this.accounts))
}
})
}
}
UniversalDApp.prototype.getBalanceInEther = function (address, callback) {
var self = this
self.getBalance(address, (error, balance) => {
if (error) {
callback(error)
getBalance (address, cb) {
address = ethJSUtil.stripHexPrefix(address)
if (!executionContext.isVM()) {
executionContext.web3().eth.getBalance(address, (err, res) => {
if (err) {
cb(err)
} else {
cb(null, res.toString(10))
}
})
} else {
callback(null, executionContext.web3().fromWei(balance, 'ether'))
if (!this.accounts) {
return cb('No accounts?')
}
executionContext.vm().stateManager.getAccountBalance(Buffer.from(address, 'hex'), (err, res) => {
if (err) {
cb('Account not found')
} else {
cb(null, new BN(res).toString(10))
}
})
}
})
}
}
UniversalDApp.prototype.pendingTransactionsCount = function () {
return Object.keys(this.txRunner.pendingTxs).length
}
getBalanceInEther (address, callback) {
this.getBalance(address, (error, balance) => {
if (error) {
callback(error)
} else {
callback(null, executionContext.web3().fromWei(balance, 'ether'))
}
})
}
/**
* deploy the given contract
*
* @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ).
* @param {Function} callback - callback.
*/
UniversalDApp.prototype.createContract = function (data, confirmationCb, continueCb, promptCb, callback) {
this.runTx({data: data, useCall: false}, confirmationCb, continueCb, promptCb, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
callback(error, txResult)
})
}
pendingTransactionsCount () {
return Object.keys(this.txRunner.pendingTxs).length
}
/**
* call the current given contract
*
* @param {String} to - address of the contract to call.
* @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ).
* @param {Object} funAbi - abi definition of the function to call.
* @param {Function} callback - callback.
*/
UniversalDApp.prototype.callFunction = function (to, data, funAbi, confirmationCb, continueCb, promptCb, callback) {
this.runTx({to: to, data: data, useCall: funAbi.constant}, confirmationCb, continueCb, promptCb, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
callback(error, txResult)
})
}
/**
* deploy the given contract
*
* @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ).
* @param {Function} callback - callback.
*/
createContract (data, confirmationCb, continueCb, promptCb, callback) {
this.runTx({data: data, useCall: false}, confirmationCb, continueCb, promptCb, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
callback(error, txResult)
})
}
UniversalDApp.prototype.context = function () {
return (executionContext.isVM() ? 'memory' : 'blockchain')
}
/**
* call the current given contract
*
* @param {String} to - address of the contract to call.
* @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ).
* @param {Object} funAbi - abi definition of the function to call.
* @param {Function} callback - callback.
*/
callFunction (to, data, funAbi, confirmationCb, continueCb, promptCb, callback) {
this.runTx({to: to, data: data, useCall: funAbi.constant}, confirmationCb, continueCb, promptCb, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
callback(error, txResult)
})
}
UniversalDApp.prototype.getABI = function (contract) {
return txHelper.sortAbiFunction(contract.abi)
}
context () {
return (executionContext.isVM() ? 'memory' : 'blockchain')
}
UniversalDApp.prototype.getFallbackInterface = function (contractABI) {
return txHelper.getFallbackInterface(contractABI)
}
getABI (contract) {
return txHelper.sortAbiFunction(contract.abi)
}
UniversalDApp.prototype.getInputs = function (funABI) {
if (!funABI.inputs) {
return ''
getFallbackInterface (contractABI) {
return txHelper.getFallbackInterface(contractABI)
}
return txHelper.inputParametersDeclarationToString(funABI.inputs)
}
/**
* This function send a tx only to javascript VM or testnet, will return an error for the mainnet
* SHOULD BE TAKEN CAREFULLY!
*
* @param {Object} tx - transaction.
*/
UniversalDApp.prototype.runTestTx = function (tx) {
return new Promise((resolve, reject) => {
executionContext.detectNetwork((error, network) => {
if (error) return reject(error)
if (network.name === 'Main' && network.id === '1') {
return reject(new Error('It is not allowed to make this action against mainnet'))
}
this.silentRunTx(tx, (error, result) => {
getInputs (funABI) {
if (!funABI.inputs) {
return ''
}
return txHelper.inputParametersDeclarationToString(funABI.inputs)
}
/**
* This function send a tx only to javascript VM or testnet, will return an error for the mainnet
* SHOULD BE TAKEN CAREFULLY!
*
* @param {Object} tx - transaction.
*/
runTestTx (tx) {
return new Promise((resolve, reject) => {
executionContext.detectNetwork((error, network) => {
if (error) return reject(error)
resolve({
transactionHash: result.transactionHash,
status: result.result.status,
gasUsed: '0x' + result.result.gasUsed.toString('hex'),
error: result.result.vm.exceptionError,
return: result.result.vm.return ? '0x' + result.result.vm.return.toString('hex') : '0x',
createdAddress: result.result.createdAddress ? '0x' + result.result.createdAddress.toString('hex') : undefined
if (network.name === 'Main' && network.id === '1') {
return reject(new Error('It is not allowed to make this action against mainnet'))
}
this.silentRunTx(tx, (error, result) => {
if (error) return reject(error)
resolve({
transactionHash: result.transactionHash,
status: result.result.status,
gasUsed: '0x' + result.result.gasUsed.toString('hex'),
error: result.result.vm.exceptionError,
return: result.result.vm.return ? '0x' + result.result.vm.return.toString('hex') : '0x',
createdAddress: result.result.createdAddress ? '0x' + result.result.createdAddress.toString('hex') : undefined
})
})
})
})
})
}
}
/**
* This function send a tx without alerting the user (if mainnet or if gas estimation too high).
* SHOULD BE TAKEN CAREFULLY!
*
* @param {Object} tx - transaction.
* @param {Function} callback - callback.
*/
UniversalDApp.prototype.silentRunTx = function (tx, cb) {
if (!executionContext.isVM()) return cb('Cannot silently send transaction through a web3 provider')
this.txRunner.rawRun(
tx,
(network, tx, gasEstimation, continueTxExecution, cancelCb) => { continueTxExecution() },
(error, continueTxExecution, cancelCb) => { if (error) { cb(error) } else { continueTxExecution() } },
(okCb, cancelCb) => { okCb() },
cb)
}
/**
* This function send a tx without alerting the user (if mainnet or if gas estimation too high).
* SHOULD BE TAKEN CAREFULLY!
*
* @param {Object} tx - transaction.
* @param {Function} callback - callback.
*/
silentRunTx (tx, cb) {
if (!executionContext.isVM()) return cb('Cannot silently send transaction through a web3 provider')
this.txRunner.rawRun(
tx,
(network, tx, gasEstimation, continueTxExecution, cancelCb) => { continueTxExecution() },
(error, continueTxExecution, cancelCb) => { if (error) { cb(error) } else { continueTxExecution() } },
(okCb, cancelCb) => { okCb() },
cb
)
}
UniversalDApp.prototype.runTx = function (args, confirmationCb, continueCb, promptCb, cb) {
const self = this
async.waterfall([
function getGasLimit (next) {
if (self.transactionContextAPI.getGasLimit) {
return self.transactionContextAPI.getGasLimit(next)
}
next(null, 3000000)
},
function queryValue (gasLimit, next) {
if (args.value) {
return next(null, args.value, gasLimit)
}
if (args.useCall || !self.transactionContextAPI.getValue) {
return next(null, 0, gasLimit)
}
self.transactionContextAPI.getValue(function (err, value) {
next(err, value, gasLimit)
})
},
function getAccount (value, gasLimit, next) {
if (args.from) {
return next(null, args.from, value, gasLimit)
}
if (self.transactionContextAPI.getAddress) {
return self.transactionContextAPI.getAddress(function (err, address) {
next(err, address, value, gasLimit)
runTx (args, confirmationCb, continueCb, promptCb, cb) {
const self = this
async.waterfall([
function getGasLimit (next) {
if (self.transactionContextAPI.getGasLimit) {
return self.transactionContextAPI.getGasLimit(next)
}
next(null, 3000000)
},
function queryValue (gasLimit, next) {
if (args.value) {
return next(null, args.value, gasLimit)
}
if (args.useCall || !self.transactionContextAPI.getValue) {
return next(null, 0, gasLimit)
}
self.transactionContextAPI.getValue(function (err, value) {
next(err, value, gasLimit)
})
}
self.getAccounts(function (err, accounts) {
let address = accounts[0]
},
function getAccount (value, gasLimit, next) {
if (args.from) {
return next(null, args.from, value, gasLimit)
}
if (self.transactionContextAPI.getAddress) {
return self.transactionContextAPI.getAddress(function (err, address) {
next(err, address, value, gasLimit)
})
}
self.getAccounts(function (err, accounts) {
let address = accounts[0]
if (err) return next(err)
if (!address) return next('No accounts available')
if (executionContext.isVM() && !self.accounts[address]) {
return next('Invalid account selected')
if (err) return next(err)
if (!address) return next('No accounts available')
if (executionContext.isVM() && !self.accounts[address]) {
return next('Invalid account selected')
}
next(null, address, value, gasLimit)
})
},
function runTransaction (fromAddress, value, gasLimit, next) {
var tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp }
var payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences }
var timestamp = Date.now()
if (tx.timestamp) {
timestamp = tx.timestamp
}
next(null, address, value, gasLimit)
})
},
function runTransaction (fromAddress, value, gasLimit, next) {
var tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp }
var payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences }
var timestamp = Date.now()
if (tx.timestamp) {
timestamp = tx.timestamp
}
self.event.trigger('initiatingTransaction', [timestamp, tx, payLoad])
self.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb,
function (error, result) {
let eventName = (tx.useCall ? 'callExecuted' : 'transactionExecuted')
self.event.trigger(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad])
self.event.trigger('initiatingTransaction', [timestamp, tx, payLoad])
self.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb,
function (error, result) {
let eventName = (tx.useCall ? 'callExecuted' : 'transactionExecuted')
self.event.trigger(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad])
if (error && (typeof (error) !== 'string')) {
if (error.message) error = error.message
else {
try { error = 'error: ' + JSON.stringify(error) } catch (e) {}
if (error && (typeof (error) !== 'string')) {
if (error.message) error = error.message
else {
try { error = 'error: ' + JSON.stringify(error) } catch (e) {}
}
}
next(error, result)
}
next(error, result)
}
)
}
], cb)
)
}
], cb)
}
}
module.exports = UniversalDApp

Loading…
Cancel
Save