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": { "dependencies": {
"http-server": "0.9.0", "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" "remixd": "0.1.8-alpha.6"
}, },
"repository": { "repository": {

@ -7,7 +7,6 @@ var async = require('async')
var request = require('request') var request = require('request')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var EventManager = require('./lib/events') var EventManager = require('./lib/events')
var EventEmitter = require('events')
var registry = require('./global/registry') var registry = require('./global/registry')
var UniversalDApp = require('./universal-dapp.js') var UniversalDApp = require('./universal-dapp.js')
var UniversalDAppUI = require('./universal-dapp-ui.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 PanelsResize from './lib/panels-resize'
import { EntityStore } from './lib/store' import { EntityStore } from './lib/store'
import { RemixAppManager } from './remixAppManager' 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 framingService from './framingService'
import { ApiFactory } from 'remix-plugin'
import { TxListenerModule } from './app/tabs/txlistener-module'
var css = csjs` var css = csjs`
html { box-sizing: border-box; } html { box-sizing: border-box; }
@ -116,8 +117,9 @@ var css = csjs`
} }
` `
class App { class App extends ApiFactory {
constructor (api = {}, events = {}, opts = {}) { constructor (api = {}, events = {}, opts = {}) {
super()
var self = this var self = this
this.event = new EventManager() this.event = new EventManager()
self._components = {} self._components = {}
@ -173,7 +175,7 @@ class App {
run.apply(self) run.apply(self)
} }
profile () { get profile () {
return { return {
name: 'app', name: 'app',
description: 'service - provides information about current context (network).', 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 that proxy is used by appManager to broadcast new transaction event
*/ */
const txListenerModuleProxy = { const txListenerModule = new TxListenerModule(txlistener)
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)
})
txlistener.startListening() txlistener.startListening()
// TODO: There are still a lot of dep between editorpanel and filemanager // 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) const appManager = new RemixAppManager(appStore)
registry.put({api: appManager, name: 'appmanager'}) 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 settings = new SettingsTab(self._components.registry)
let analysis = new AnalysisTab(registry) let analysis = new AnalysisTab(registry)
let debug = new DebuggerTab() let debug = new DebuggerTab()
const landingPage = new LandingPage(appManager, appStore)
// let support = new SupportTab() // let support = new SupportTab()
let test = new TestTab(self._components.registry, compileTab) let test = new TestTab(self._components.registry, compileTab)
let sourceHighlighters = registry.get('editor').api.sourceHighlighters let sourceHighlighters = registry.get('editor').api.sourceHighlighters
let configProvider = self._components.filesProviders['config'] let configProvider = self._components.filesProviders['config']
appManager.init([ appManager.init([
{ profile: homepageProfile(), api: generateHomePage(appManager, appStore) }, this.api(),
{ profile: this.profile(), api: this }, landingPage.api(),
{ profile: udapp.profile(), api: udapp }, udapp.api(),
{ profile: fileManager.profile(), api: fileManager }, fileManager.api(),
{ profile: sourceHighlighters.profile(), api: sourceHighlighters }, sourceHighlighters.api(),
{ profile: configProvider.profile(), api: configProvider }, configProvider.api(),
{ profile: txListenerModuleProxy.profile(), api: txListenerModuleProxy }, txListenerModule.api(),
{ profile: filePanel.profile(), api: filePanel }, filePanel.api(),
// { profile: support.profile(), api: support }, // { profile: support.profile(), api: support },
{ profile: settings.profile(), api: settings }, settings.api(),
{ profile: pluginManagerComponent.profile(), api: pluginManagerComponent }]) pluginManagerComponent.api()
])
appManager.registerMany([ appManager.registerMany([
{ profile: compileTab.profile(), api: compileTab }, compileTab.api(),
{ profile: run.profile(), api: run }, run.api(),
{ profile: debug.profile(), api: debug }, debug.api(),
{ profile: analysis.profile(), api: analysis }, analysis.api(),
{ profile: test.profile(), api: test }, test.api(),
{ profile: filePanel.remixdHandle.profile(), api: filePanel.remixdHandle } filePanel.remixdHandle.api(),
...appManager.plugins()
]) ])
appManager.registerMany(appManager.plugins())
framingService.start(appStore, swapPanelApi, verticalIconsApi, mainPanelApi, this._components.resizeFeature) 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 var txLogger = new TxLogger() // eslint-disable-line
txLogger.event.register('debuggingRequested', (hash) => { txLogger.event.register('debuggingRequested', (hash) => {
if (!appStore.isActive('debugger')) appManager.activateOne('debugger') if (!appStore.isActive('debugger')) appManager.activateOne('debugger')
appStore.getOne('debugger').api.debugger().debug(hash) debug.debugger().debug(hash)
verticalIconsApi.select('debugger') verticalIconsApi.select('debugger')
}) })

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

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

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

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

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

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

@ -2,21 +2,35 @@
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
function FilesTree (name, storage) { import { ApiFactory } from 'remix-plugin'
var self = this
var event = new EventManager() class FilesTree extends ApiFactory {
this.event = event
this.type = name constructor (name, storage) {
this.structFile = '.' + name + '.tree' super()
this.tree = {} this.event = new EventManager()
this.storage = storage
this.exists = function (path, cb) { 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)) cb(null, this._exists(path))
} }
function updateRefs (path, type) { updateRefs (path, type) {
var split = path.split('/') // this should be unprefixed path var split = path.split('/') // this should be unprefixed path
var crawlpath = self.tree var crawlpath = this.tree
var intermediatePath = '' var intermediatePath = ''
split.forEach((pathPart, index) => { split.forEach((pathPart, index) => {
intermediatePath += pathPart intermediatePath += pathPart
@ -30,91 +44,90 @@ function FilesTree (name, storage) {
delete crawlpath[intermediatePath] 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) var unprefixedpath = this.removePrefix(path)
return storage.exists(unprefixedpath) return this.storage.exists(unprefixedpath)
} }
this.init = function (cb) { init (cb) {
var tree = storage.get(this.structFile) var tree = this.storage.get(this.structFile)
this.tree = tree ? JSON.parse(tree) : {} this.tree = tree ? JSON.parse(tree) : {}
if (cb) cb() if (cb) cb()
} }
this.get = function (path, cb) { get (path, cb) {
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
var content = storage.get(unprefixedpath) var content = this.storage.get(unprefixedpath)
if (cb) { if (cb) {
cb(null, content) cb(null, content)
} }
return content return content
} }
this.set = function (path, content, cb) { set (path, content, cb) {
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
updateRefs(unprefixedpath, 'add') this.updateRefs(unprefixedpath, 'add')
var exists = storage.exists(unprefixedpath) var exists = this.storage.exists(unprefixedpath)
if (!storage.set(unprefixedpath, content)) { if (!this.storage.set(unprefixedpath, content)) {
if (cb) cb('error updating ' + path) if (cb) cb('error updating ' + path)
return false return false
} }
if (!exists) { if (!exists) {
event.trigger('fileAdded', [this.type + '/' + unprefixedpath, false]) this.event.trigger('fileAdded', [this.type + '/' + unprefixedpath, false])
} else { } else {
event.trigger('fileChanged', [this.type + '/' + unprefixedpath]) this.event.trigger('fileChanged', [this.type + '/' + unprefixedpath])
} }
if (cb) cb() if (cb) cb()
return true return true
} }
this.addReadOnly = function (path, content) { addReadOnly (path, content) {
return this.set(path, content) return this.set(path, content)
} }
this.isReadOnly = function (path) { isReadOnly (path) {
return false return false
} }
this.remove = function (path) { remove (path) {
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
updateRefs(unprefixedpath, 'remove') this.updateRefs(unprefixedpath, 'remove')
if (!this._exists(unprefixedpath)) { if (!this._exists(unprefixedpath)) {
return false return false
} }
if (!storage.remove(unprefixedpath)) { if (!this.storage.remove(unprefixedpath)) {
return false return false
} }
event.trigger('fileRemoved', [this.type + '/' + unprefixedpath]) this.event.trigger('fileRemoved', [this.type + '/' + unprefixedpath])
return true return true
} }
this.rename = function (oldPath, newPath, isFolder) { rename (oldPath, newPath, isFolder) {
var unprefixedoldPath = this.removePrefix(oldPath) var unprefixedoldPath = this.removePrefix(oldPath)
var unprefixednewPath = this.removePrefix(newPath) var unprefixednewPath = this.removePrefix(newPath)
updateRefs(unprefixedoldPath, 'remove') this.updateRefs(unprefixedoldPath, 'remove')
updateRefs(unprefixednewPath, 'add') this.updateRefs(unprefixednewPath, 'add')
if (storage.exists(unprefixedoldPath)) { if (this.storage.exists(unprefixedoldPath)) {
if (!storage.rename(unprefixedoldPath, unprefixednewPath)) { if (!this.storage.rename(unprefixedoldPath, unprefixednewPath)) {
return false 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 true
} }
return false return false
} }
this.resolveDirectory = function (path, callback) { resolveDirectory (path, callback) {
var self = this
if (path[0] === '/') path = path.substring(1) if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [self.type]: { } }) if (!path) return callback(null, { [this.type]: { } })
var tree = {} var tree = {}
path = self.removePrefix(path) path = this.removePrefix(path)
var split = path.split('/') // this should be unprefixed path var split = path.split('/') // this should be unprefixed path
var crawlpath = self.tree var crawlpath = this.tree
split.forEach((pathPart, index) => { split.forEach((pathPart, index) => {
if (crawlpath[pathPart]) crawlpath = crawlpath[pathPart] if (crawlpath[pathPart]) crawlpath = crawlpath[pathPart]
}) })
@ -125,20 +138,12 @@ function FilesTree (name, storage) {
callback(null, tree) callback(null, tree)
} }
this.removePrefix = function (path) { removePrefix (path) {
path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path
if (path[0] === '/') return path.substring(1) if (path[0] === '/') return path.substring(1)
return path 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 module.exports = FilesTree

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

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

@ -52,13 +52,15 @@ export class TabProxy {
appStore.event.on('activate', (name) => { appStore.event.on('activate', (name) => {
const { profile } = appStore.getOne(name) const { profile } = appStore.getOne(name)
if (profile.prefferedLocation === 'mainPanel') { if (profile.location === 'mainPanel') {
this.addTab(name, () => { this.addTab(
this.event.emit('switchApp', name) name,
}, () => { () => this.event.emit('switchApp', name),
this.event.emit('closeApp', name) () => {
this.appManager.deactivateOne(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 EventManager = require('../../lib/events')
var css = require('./styles/analysis-tab-styles') var css = require('./styles/analysis-tab-styles')
class AnalysisTab { import { ApiFactory } from 'remix-plugin'
class AnalysisTab extends ApiFactory {
constructor (registry) { constructor (registry) {
super()
this.event = new EventManager() this.event = new EventManager()
this.registry = registry this.registry = registry
} }
profile () { get profile () {
return { return {
name: 'solidityStaticAnalysis', name: 'solidityStaticAnalysis',
displayName: 'solidity static analysis', displayName: 'solidity static analysis',

@ -17,9 +17,12 @@ var css = require('./styles/compile-tab-styles')
const CompileTabLogic = require('./compileTab/compileTab.js') const CompileTabLogic = require('./compileTab/compileTab.js')
const CompilerContainer = require('./compileTab/compilerContainer.js') const CompilerContainer = require('./compileTab/compilerContainer.js')
class CompileTab { import { ApiFactory } from 'remix-plugin'
class CompileTab extends ApiFactory {
constructor (registry) { constructor (registry) {
super()
this.events = new EventEmitter() this.events = new EventEmitter()
this._view = { this._view = {
el: null, el: null,
@ -56,12 +59,16 @@ class CompileTab {
) )
} }
activate () { get profile () {
this.listenToEvents() return {
this.compilerContainer.activate() displayName: 'solidity compiler',
} name: 'solidity',
methods: ['getCompilationResult'],
deactivate () { 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 () { getCompilationResult () {
return new Promise((resolve, reject) => { return this.compileTabLogic.compiler.lastCompilationResult
resolve(this.compileTabLogic.compiler.lastCompilationResult)
})
} }
/********* /*********
@ -340,6 +333,8 @@ class CompileTab {
render () { render () {
if (this._view.el) return this._view.el if (this._view.el) return this._view.el
this.listenToEvents()
this.compilerContainer.activate()
this._view.errorContainer = yo`<div></div>` this._view.errorContainer = yo`<div></div>`
this._view.contractSelection = this.contractSelection() this._view.contractSelection = this.contractSelection()

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

@ -12,9 +12,12 @@ var ContractDropdownUI = require('./runTab/contractDropdown.js')
var Recorder = require('./runTab/model/recorder.js') var Recorder = require('./runTab/model/recorder.js')
var RecorderUI = require('./runTab/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) { constructor (udapp, udappUI, config, fileManager, editor, logCallback, filePanel, pluginManager, compilersArtefacts) {
super()
this.event = new EventManager() this.event = new EventManager()
this.renderInstanceContainer() this.renderInstanceContainer()
@ -25,6 +28,18 @@ class RunTab {
this.renderContainer() 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 () { renderContainer () {
this.container = yo`<div class="${css.runTabView}" id="runTabView" ></div>` this.container = yo`<div class="${css.runTabView}" id="runTabView" ></div>`
@ -147,17 +162,6 @@ class RunTab {
return this.container 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 module.exports = RunTab

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

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

@ -7,8 +7,11 @@ var globalRegistry = require('../../global/registry')
var css = require('./styles/test-tab-styles') var css = require('./styles/test-tab-styles')
var remixTests = require('remix-tests') var remixTests = require('remix-tests')
module.exports = class TestTab { import { ApiFactory } from 'remix-plugin'
module.exports = class TestTab extends ApiFactory {
constructor (localRegistry, compileTab) { constructor (localRegistry, compileTab) {
super()
// TODO here is a direct reference to compile tab, should be removed // TODO here is a direct reference to compile tab, should be removed
const self = this const self = this
self.compileTab = compileTab self.compileTab = compileTab
@ -23,7 +26,8 @@ module.exports = class TestTab {
self.data = {} self.data = {}
self.testList = yo`<div class=${css.testList}></div>` self.testList = yo`<div class=${css.testList}></div>`
} }
profile () {
get profile () {
return { return {
name: 'solidityUnitTesting', name: 'solidityUnitTesting',
displayName: 'solidity unit testing', displayName: 'solidity unit testing',
@ -33,6 +37,7 @@ module.exports = class TestTab {
description: ' - ' description: ' - '
} }
} }
render () { render () {
const self = this 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>` 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 { import { defaultWorkspaces } from './workspace'
constructor (sections) { import { ApiFactory } from 'remix-plugin'
this.sections = sections 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 () { render () {
@ -48,5 +118,3 @@ class LandingPage {
return totalLook return totalLook
} }
} }
module.exports = LandingPage

@ -1,13 +1,10 @@
export default { export default {
start: (appStore, swapPanelApi, verticalIconApi, mainPanelApi, resizeFeature) => { start: (appStore, swapPanelApi, verticalIconApi, mainPanelApi, resizeFeature) => {
swapPanelApi.event.on('toggle', (moduleName) => { swapPanelApi.event.on('toggle', () => {
resizeFeature.panel1.clientWidth !== 0 ? resizeFeature.minimize() : resizeFeature.maximise() resizeFeature.panel1.clientWidth !== 0 ? resizeFeature.minimize() : resizeFeature.maximise()
}) })
swapPanelApi.event.on('showing', (moduleName) => { swapPanelApi.event.on('showing', () => {
resizeFeature.panel1.clientWidth === 0 ? resizeFeature.maximise() : '' 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', () => { mainPanelApi.event.on('toggle', () => {
resizeFeature.maximise() resizeFeature.maximise()

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

@ -14,8 +14,8 @@ export class RemixAppManager extends AppManagerApi {
} }
} }
ensureActivated (module) { ensureActivated (apiName) {
if (!this.store.isActive(module)) this.activateOne(module) if (!this.store.isActive(apiName)) this.activateOne(apiName)
} }
proxy () { proxy () {
@ -24,33 +24,23 @@ export class RemixAppManager extends AppManagerApi {
} }
setActive (name, isActive) { setActive (name, isActive) {
const entity = this.getEntity(name) const api = this.getEntity(name)
// temp // temp
if (entity && (name === 'solidity' || name === 'vyper')) { if (api && (name === 'solidity' || name === 'vyper')) {
isActive ? this.data.proxy.register(name, entity.api) : this.data.proxy.unregister(name, entity.api) isActive ? this.data.proxy.register(name, api) : this.data.proxy.unregister(name, api)
} }
isActive ? this.store.activate(name) : this.store.deactivate(name) isActive ? this.store.activate(name) : this.store.deactivate(name)
if (!isActive) { if (!isActive) {
this.removeHiddenServices(entity) this.removeHiddenServices(api)
} }
} }
getEntity (entityName) { getEntity (apiName) {
return this.store.getOne(entityName) return this.store.getOne(apiName)
} }
addEntity (entity) { addEntity (api) {
this.store.add(entity.profile.name, entity) this.store.add(api)
}
// 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)
}
} }
removeHiddenServices (profile) { removeHiddenServices (profile) {
@ -81,10 +71,11 @@ export class RemixAppManager extends AppManagerApi {
url: 'https://pipeline-alpha.pipeos.one', url: 'https://pipeline-alpha.pipeos.one',
description: 'Visual IDE for contracts and dapps', description: 'Visual IDE for contracts and dapps',
icon: 'data:image/png;base64,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', icon: 'data:image/png;base64,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',
prefferedLocation: 'mainPanel' location: 'mainPanel'
} }
const plugins = [{ profile: pipeline, api: new Plugin(pipeline, { resolveLocaton: (iframe) => { return this.resolveLocation(pipeline, iframe) } }) }, return [
{ profile: vyper, api: new Plugin(vyper, { resolveLocaton: (iframe) => { return this.resolveLocation(vyper, iframe) } }) }] new Plugin(pipeline),
return plugins new Plugin(vyper)
]
} }
} }

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

Loading…
Cancel
Save