new remix-plugin engine

pull/3094/head
yann300 5 years ago
parent dc734ea482
commit 48c009e184
  1. 2
      package.json
  2. 388
      src/app.js
  3. 33
      src/app/compiler/compiler-artefacts.js
  4. 23
      src/app/compiler/compiler-imports.js
  5. 13
      src/app/components/hidden-panel.js
  6. 14
      src/app/components/main-panel.js
  7. 66
      src/app/components/panel.js
  8. 39
      src/app/components/plugin-manager-component.js
  9. 36
      src/app/components/plugin-manager-proxy.js
  10. 46
      src/app/components/side-panel.js
  11. 72
      src/app/components/vertical-icons.js
  12. 10
      src/app/editor/SourceHighlighters.js
  13. 29
      src/app/editor/contextualListener.js
  14. 10
      src/app/editor/editor.js
  15. 10
      src/app/files/browser-files-tree.js
  16. 78
      src/app/files/compiler-metadata.js
  17. 59
      src/app/files/fileManager.js
  18. 11
      src/app/files/remixd-handle.js
  19. 30
      src/app/panels/file-panel.js
  20. 75
      src/app/panels/main-view.js
  21. 8
      src/app/panels/tab-proxy.js
  22. 31
      src/app/panels/terminal.js
  23. 14
      src/app/tabs/analysis-tab.js
  24. 34
      src/app/tabs/compile-tab.js
  25. 0
      src/app/tabs/compileTab/contractParser.js
  26. 13
      src/app/tabs/debugger-tab.js
  27. 10
      src/app/tabs/debugger/debuggerUI.js
  28. 2
      src/app/tabs/debugger/debuggerUI/ButtonNavigator.js
  29. 2
      src/app/tabs/debugger/debuggerUI/Slider.js
  30. 2
      src/app/tabs/debugger/debuggerUI/StepManager.js
  31. 2
      src/app/tabs/debugger/debuggerUI/TxBrowser.js
  32. 0
      src/app/tabs/debugger/debuggerUI/VmDebugger.js
  33. 0
      src/app/tabs/debugger/debuggerUI/styles/basicStyles.js
  34. 0
      src/app/tabs/debugger/debuggerUI/styles/dropdownPanel.js
  35. 0
      src/app/tabs/debugger/debuggerUI/styles/sliderStyles.js
  36. 0
      src/app/tabs/debugger/debuggerUI/styles/treeView.js
  37. 0
      src/app/tabs/debugger/debuggerUI/vmDebugger/CalldataPanel.js
  38. 0
      src/app/tabs/debugger/debuggerUI/vmDebugger/CallstackPanel.js
  39. 2
      src/app/tabs/debugger/debuggerUI/vmDebugger/CodeListView.js
  40. 4
      src/app/tabs/debugger/debuggerUI/vmDebugger/DropdownPanel.js
  41. 0
      src/app/tabs/debugger/debuggerUI/vmDebugger/FullStoragesChanges.js
  42. 0
      src/app/tabs/debugger/debuggerUI/vmDebugger/MemoryPanel.js
  43. 2
      src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityLocals.js
  44. 0
      src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityState.js
  45. 0
      src/app/tabs/debugger/debuggerUI/vmDebugger/StackPanel.js
  46. 0
      src/app/tabs/debugger/debuggerUI/vmDebugger/StepDetail.js
  47. 0
      src/app/tabs/debugger/debuggerUI/vmDebugger/StoragePanel.js
  48. 0
      src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js
  49. 8
      src/app/tabs/network-module.js
  50. 49
      src/app/tabs/run-tab.js
  51. 4
      src/app/tabs/runTab/contractDropdown.js
  52. 41
      src/app/tabs/runTab/model/dropdownlogic.js
  53. 2
      src/app/tabs/runTab/recorder.js
  54. 10
      src/app/tabs/runTab/settings.js
  55. 4
      src/app/tabs/settings-tab.js
  56. 18
      src/app/tabs/staticanalysis/staticAnalysisView.js
  57. 0
      src/app/tabs/staticanalysis/styles/staticAnalysisView-styles.js
  58. 27
      src/app/tabs/test-tab.js
  59. 7
      src/app/tabs/theme-module.js
  60. 6
      src/app/ui/auto-complete-popup.js
  61. 2
      src/app/ui/confirmDialog.js
  62. 35
      src/app/ui/landing-page/landing-page.js
  63. 4
      src/app/ui/landing-page/workspace.js
  64. 4
      src/app/ui/multiParamManager.js
  65. 4
      src/app/ui/persmission-handler.js
  66. 37
      src/app/ui/txLogger.js
  67. 36
      src/app/ui/universal-dapp-ui.js
  68. 34
      src/framingService.js
  69. 7
      src/lib/cmdInterpreterAPI.js
  70. 0
      src/lib/commands.js
  71. 21
      src/lib/gist-handler.js
  72. 52
      src/lib/offsetToLineColumnConverter.js
  73. 0
      src/lib/publishOnSwarm.js
  74. 289
      src/lib/store.js
  75. 2
      src/lib/transactionReceiptResolver.js
  76. 22
      src/loadFilesFromParent.js
  77. 48
      src/makeUdapp.js
  78. 11
      src/module-id.js
  79. 84
      src/remixAppManager.js
  80. 7
      src/universal-dapp.js

@ -74,7 +74,7 @@
}, },
"dependencies": { "dependencies": {
"http-server": "^0.11.1", "http-server": "^0.11.1",
"remix-plugin": "0.0.2-alpha.10", "@remixproject/engine": "^0.1.2-7",
"remixd": "0.1.8-alpha.6" "remixd": "0.1.8-alpha.6"
}, },
"repository": { "repository": {

@ -2,38 +2,32 @@
'use strict' 'use strict'
var isElectron = require('is-electron') var isElectron = require('is-electron')
var $ = require('jquery')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var yo = require('yo-yo') var yo = require('yo-yo')
var async = require('async')
var request = require('request')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var registry = require('./global/registry') var registry = require('./global/registry')
var UniversalDApp = require('./universal-dapp.js')
var UniversalDAppUI = require('./universal-dapp-ui.js')
var Remixd = require('./lib/remixd') var Remixd = require('./lib/remixd')
var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter') var loadFileFromParent = require('./loadFilesFromParent')
var { OffsetToLineColumnConverter } = require('./lib/offsetToLineColumnConverter')
var QueryParams = require('./lib/query-params') var QueryParams = require('./lib/query-params')
var GistHandler = require('./lib/gist-handler') var GistHandler = require('./lib/gist-handler')
var helper = require('./lib/helper') var makeUdapp = require('./makeUdapp')
var Storage = remixLib.Storage var Storage = remixLib.Storage
var Browserfiles = require('./app/files/browser-files') var Browserfiles = require('./app/files/browser-files')
var SharedFolder = require('./app/files/shared-folder') var SharedFolder = require('./app/files/shared-folder')
var Config = require('./config') var Config = require('./config')
var Renderer = require('./app/ui/renderer') var Renderer = require('./app/ui/renderer')
var executionContext = require('./execution-context')
var examples = require('./app/editor/example-contracts') var examples = require('./app/editor/example-contracts')
var modalDialogCustom = require('./app/ui/modal-dialog-custom') var modalDialogCustom = require('./app/ui/modal-dialog-custom')
var TxLogger = require('./app/execution/txLogger')
var Txlistener = remixLib.execution.txListener
var EventsDecoder = remixLib.execution.EventsDecoder
var FileManager = require('./app/files/fileManager') var FileManager = require('./app/files/fileManager')
var BasicReadOnlyExplorer = require('./app/files/basicReadOnlyExplorer') var BasicReadOnlyExplorer = require('./app/files/basicReadOnlyExplorer')
var NotPersistedExplorer = require('./app/files/NotPersistedExplorer') var NotPersistedExplorer = require('./app/files/NotPersistedExplorer')
var toolTip = require('./app/ui/tooltip') var toolTip = require('./app/ui/tooltip')
var TransactionReceiptResolver = require('./transactionReceiptResolver') var CompilerMetadata = require('./app/files/compiler-metadata')
var CompilerImport = require('./app/compiler/compiler-imports')
const PluginManagerComponent = require('./app/components/plugin-manager-component') const PluginManagerComponent = require('./app/components/plugin-manager-component')
const CompilersArtefacts = require('./app/compiler/compiler-artefacts')
const CompileTab = require('./app/tabs/compile-tab') const CompileTab = require('./app/tabs/compile-tab')
const SettingsTab = require('./app/tabs/settings-tab') const SettingsTab = require('./app/tabs/settings-tab')
@ -42,19 +36,19 @@ const DebuggerTab = require('./app/tabs/debugger-tab')
const TestTab = require('./app/tabs/test-tab') const TestTab = require('./app/tabs/test-tab')
const RunTab = require('./app/tabs/run-tab') const RunTab = require('./app/tabs/run-tab')
const FilePanel = require('./app/panels/file-panel') const FilePanel = require('./app/panels/file-panel')
const Editor = require('./app/editor/editor')
import PanelsResize from './lib/panels-resize' import PanelsResize from './lib/panels-resize'
import { EntityStore } from './lib/store'
import { RemixAppManager } from './remixAppManager' import { RemixAppManager } from './remixAppManager'
import { LandingPage } from './app/ui/landing-page/landing-page' import { FramingService } from './framingService'
import framingService from './framingService'
import { MainView } from './app/panels/main-view' import { MainView } from './app/panels/main-view'
import { ThemeModule } from './app/tabs/theme-module' import { ThemeModule } from './app/tabs/theme-module'
import { NetworkModule } from './app/tabs/network-module' import { NetworkModule } from './app/tabs/network-module'
import { SidePanel } from './app/components/side-panel' import { SidePanel } from './app/components/side-panel'
import { MainPanel } from './app/components/main-panel'
import { HiddenPanel } from './app/components/hidden-panel' import { HiddenPanel } from './app/components/hidden-panel'
import { VerticalIcons } from './app/components/vertical-icons' import { VerticalIcons } from './app/components/vertical-icons'
import { LandingPage } from './app/ui/landing-page/landing-page'
import { MainPanel } from './app/components/main-panel'
var css = csjs` var css = csjs`
html { box-sizing: border-box; } html { box-sizing: border-box; }
@ -119,19 +113,15 @@ class App {
constructor (api = {}, events = {}, opts = {}) { constructor (api = {}, events = {}, opts = {}) {
var self = this var self = this
self._components = {} self._components = {}
registry.put({api: self, name: 'app'}) // setup storage
var fileStorage = new Storage('sol:') var fileStorage = new Storage('sol:')
registry.put({api: fileStorage, name: 'fileStorage'})
var configStorage = new Storage('config-v0.8:') var configStorage = new Storage('config-v0.8:')
registry.put({api: configStorage, name: 'configStorage'})
self._components.config = new Config(configStorage)
registry.put({api: self._components.config, name: 'config'})
self._components.gistHandler = new GistHandler() // load app config
const config = new Config(configStorage)
registry.put({api: config, name: 'config'})
// load file system
self._components.filesProviders = {} self._components.filesProviders = {}
self._components.filesProviders['browser'] = new Browserfiles(fileStorage) self._components.filesProviders['browser'] = new Browserfiles(fileStorage)
registry.put({api: self._components.filesProviders['browser'], name: 'fileproviders/browser'}) registry.put({api: self._components.filesProviders['browser'], name: 'fileproviders/browser'})
@ -198,44 +188,6 @@ class App {
` `
return self._view.el return self._view.el
} }
loadFromGist (params) {
const self = this
return self._components.gistHandler.handleLoad(params, function (gistId) {
request.get({
url: `https://api.github.com/gists/${gistId}`,
json: true
}, (error, response, data = {}) => {
if (error || !data.files) {
modalDialogCustom.alert(`Gist load error: ${error || data.message}`)
return
}
self.loadFiles(data.files, 'gist', (errorLoadingFile) => {
if (!errorLoadingFile) self._components.filesProviders['gist'].id = gistId
})
})
})
}
loadFiles (filesSet, fileProvider, callback) {
const self = this
if (!fileProvider) fileProvider = 'browser'
async.each(Object.keys(filesSet), (file, callback) => {
helper.createNonClashingName(file, self._components.filesProviders[fileProvider],
(error, name) => {
if (error) {
modalDialogCustom.alert('Unexpected error loading the file ' + error)
} else if (helper.checkSpecialChars(name)) {
modalDialogCustom.alert('Special characters are not allowed')
} else {
self._components.filesProviders[fileProvider].set(name, filesSet[file].content)
}
callback()
})
}, (error) => {
if (!error) self._components.fileManager.switchFile()
if (callback) callback(error)
})
}
} }
module.exports = App module.exports = App
@ -243,6 +195,7 @@ module.exports = App
function run () { function run () {
var self = this var self = this
// check the origin and warn message
if (window.location.hostname === 'yann300.github.io') { if (window.location.hostname === 'yann300.github.io') {
modalDialogCustom.alert('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.') modalDialogCustom.alert('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.')
} else if (window.location.hostname === 'remix-alpha.ethereum.org' || } else if (window.location.hostname === 'remix-alpha.ethereum.org' ||
@ -256,10 +209,11 @@ function run () {
This instance of Remix you are visiting WILL NOT BE UPDATED.\n This instance of Remix you are visiting WILL NOT BE UPDATED.\n
Please make a backup of your contracts and start using http://remix.ethereum.org`) Please make a backup of your contracts and start using http://remix.ethereum.org`)
} }
if (window.location.protocol.indexOf('https') === 0) { if (window.location.protocol.indexOf('https') === 0) {
toolTip('You are using an `https` connection. Please switch to `http` if you are using Remix against an `http Web3 provider` or allow Mixed Content in your browser.') toolTip('You are using an `https` connection. Please switch to `http` if you are using Remix against an `http Web3 provider` or allow Mixed Content in your browser.')
} }
// workaround for Electron support
if (!isElectron()) { if (!isElectron()) {
// Oops! Accidentally trigger refresh or bookmark. // Oops! Accidentally trigger refresh or bookmark.
window.onbeforeunload = function () { window.onbeforeunload = function () {
@ -267,248 +221,142 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
} }
} }
let appStore = new EntityStore('module', 'name') // APP_MANAGER
// Get workspace before creating the App Manager const appManager = new RemixAppManager([])
const workspace = JSON.parse(localStorage.getItem('workspace')) const workspace = JSON.parse(localStorage.getItem('workspace'))
const appManager = new RemixAppManager(appStore)
registry.put({api: appManager, name: 'appmanager'})
registry.put({api: msg => self._components.mainview.logHtmlMessage(msg), name: 'logCallback'})
// helper for converting offset to line/column // SERVICES
var offsetToLineColumnConverter = new OffsetToLineColumnConverter(appManager) // ----------------- import content servive ----------------------------
registry.put({api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter'}) const contentImport = new CompilerImport()
// ----------------- theme servive ----------------------------
// json structure for hosting the last compilattion result
self._components.compilersArtefacts = {} // store all the possible compilation data (key represent a compiler name)
registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'})
// ----------------- UniversalDApp -----------------
const udapp = new UniversalDApp(registry)
// TODO: to remove when possible
registry.put({api: udapp, name: 'udapp'})
udapp.event.register('transactionBroadcasted', (txhash, networkName) => {
var txLink = executionContext.txDetailsLink(networkName, txhash)
if (txLink) registry.get('logCallback').api(yo`<a href="${txLink}" target="_blank">${txLink}</a>`)
})
const udappUI = new UniversalDAppUI(udapp, registry)
// TODO: to remove when possible
registry.put({api: udappUI, name: 'udappUI'})
// ----------------- Tx listener -----------------
const transactionReceiptResolver = new TransactionReceiptResolver()
const txlistener = new Txlistener({
api: {
contracts: function () {
if (self._components.compilersArtefacts['__last']) return self._components.compilersArtefacts['__last'].getContracts()
return null
},
resolveReceipt: function (tx, cb) {
transactionReceiptResolver.resolve(tx, cb)
}
},
event: {
udapp: udapp.event
}})
registry.put({api: txlistener, name: 'txlistener'})
udapp.startListening(txlistener)
const eventsDecoder = new EventsDecoder({
api: {
resolveReceipt: function (tx, cb) {
transactionReceiptResolver.resolve(tx, cb)
}
}
})
registry.put({api: eventsDecoder, name: 'eventsdecoder'})
txlistener.startListening()
// TODO: There are still a lot of dep between mainview and filemanager
// ----------------- file manager ----------------------------
self._components.fileManager = new FileManager()
const fileManager = self._components.fileManager
registry.put({api: fileManager, name: 'filemanager'})
// ----------------- Network ----------------------------
const networkModule = new NetworkModule()
registry.put({api: networkModule, name: 'network'})
// ----------------- theme module ----------------------------
const themeModule = new ThemeModule(registry) const themeModule = new ThemeModule(registry)
registry.put({api: themeModule, name: 'themeModule'}) registry.put({api: themeModule, name: 'themeModule'})
// ----------------- editor servive ----------------------------
const editor = new Editor({}, themeModule) // wrapper around ace editor
registry.put({api: editor, name: 'editor'})
editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile())
// ----------------- fileManager servive ----------------------------
const fileManager = new FileManager(editor)
registry.put({api: fileManager, name: 'filemanager'})
// ----------------- compilation metadata generation servive ----------------------------
const compilerMetadataGenerator = new CompilerMetadata(fileManager, registry.get('config').api)
// ----------------- compilation result service (can keep track of compilation results) ----------------------------
const compilersArtefacts = new CompilersArtefacts() // store all the compilation results (key represent a compiler name)
registry.put({api: compilersArtefacts, name: 'compilersartefacts'})
// ----------------- universal dapp: run transaction, listen on transactions, decode events
const {udapp, eventsDecoder, txlistener} = makeUdapp(compilersArtefacts, (domEl) => mainview.getTerminal().logHtml(domEl))
// ----------------- network service (resolve network id / name) ----------------------------
const networkModule = new NetworkModule()
// ----------------- convert offset to line/column service ----------------------------
var offsetToLineColumnConverter = new OffsetToLineColumnConverter()
registry.put({api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter'})
// ----------------- landing page ---------------------------- appManager.register([
// Need to have Home initialized before VerticalIconComponent render to access profile of it for icon contentImport,
const landingPage = new LandingPage(appManager, appStore) themeModule,
editor.sourceHighlighters,
// ----------------- Vertical Icon ---------------------------- fileManager,
const verticalIcons = new VerticalIcons('sidePanel', appStore, landingPage) compilerMetadataGenerator,
registry.put({api: verticalIcons, name: 'verticalicon'}) compilersArtefacts,
udapp,
const sidePanel = new SidePanel(appStore) networkModule,
const mainPanel = new MainPanel(appStore) offsetToLineColumnConverter
const hiddenPanel = new HiddenPanel(appStore) ])
appManager.activate(['contentImport', 'theme', 'sourceHighlighters', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'udapp', 'network', 'offsetToLineColumnConverter'])
// ----------------- main view ----------------------
self._components.mainview = new MainView(appStore, appManager, mainPanel)
registry.put({ api: self._components.mainview, name: 'mainview' })
// ----------------- Renderer -----------------
const renderer = new Renderer()
registry.put({api: renderer, name: 'renderer'})
// ----------------- app manager ----------------------------
/*
TODOs:
- for each activated plugin,
an internal module (associated only with the plugin) should be created for accessing specific part of the UI. detail to be discussed
- the current API is not optimal. For instance methods of `app` only refers to `executionContext`, wich does not make really sense.
*/
// TODOs those are instanciated before hand. should be instanciated on demand
const pluginManagerComponent = new PluginManagerComponent()
registry.put({api: appManager.proxy(), name: 'pluginmanager'})
pluginManagerComponent.setApp(appManager) // LAYOUT & SYSTEM VIEWS
pluginManagerComponent.setStore(appStore) const appPanel = new MainPanel()
const mainview = new MainView(editor, appPanel, fileManager, appManager, txlistener, eventsDecoder)
registry.put({ api: mainview, name: 'mainview' })
self._components.mainview.init() appManager.register([
appPanel
])
appManager.activate(['mainPanel'])
// those views depend on app_manager
const menuicons = new VerticalIcons(appManager)
const landingPage = new LandingPage(appManager, menuicons)
const sidePanel = new SidePanel(appManager, menuicons)
const hiddenPanel = new HiddenPanel()
const pluginManagerComponent = new PluginManagerComponent(appManager)
const filePanel = new FilePanel(appManager)
let settings = new SettingsTab(
registry.get('config').api,
editor,
appManager
)
self._components.fileManager.init() // adding Views to the DOM
self._view.mainpanel.appendChild(self._components.mainview.render()) self._view.mainpanel.appendChild(mainview.render())
self._view.iconpanel.appendChild(verticalIcons.render()) self._view.iconpanel.appendChild(menuicons.render())
self._view.sidepanel.appendChild(sidePanel.render()) self._view.sidepanel.appendChild(sidePanel.render())
document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body
let filePanel = new FilePanel() appManager.register([
registry.put({api: filePanel, name: 'filepanel'}) menuicons,
landingPage,
sidePanel,
pluginManagerComponent,
filePanel,
settings
])
appManager.activate(['menuicons', 'home', 'sidePanel', 'pluginManager', 'fileExplorers', 'settings'])
// CONTENT VIEWS & DEFAULT PLUGINS
let compileTab = new CompileTab( let compileTab = new CompileTab(
registry.get('editor').api, editor,
registry.get('config').api, registry.get('config').api,
registry.get('renderer').api, new Renderer(),
registry.get('fileproviders/swarm').api, registry.get('fileproviders/swarm').api,
registry.get('filemanager').api, registry.get('filemanager').api,
registry.get('fileproviders').api, registry.get('fileproviders').api,
registry.get('pluginmanager').api
) )
let run = new RunTab( let run = new RunTab(
registry.get('udapp').api, udapp,
registry.get('udappUI').api,
registry.get('config').api, registry.get('config').api,
registry.get('filemanager').api, registry.get('filemanager').api,
registry.get('editor').api, registry.get('editor').api,
registry.get('logCallback').api, filePanel,
registry.get('filepanel').api, registry.get('compilersartefacts').api,
registry.get('pluginmanager').api, networkModule,
registry.get('compilersartefacts').api mainview
)
let settings = new SettingsTab(
registry.get('config').api,
registry.get('editor').api,
appManager
) )
let analysis = new AnalysisTab(registry) let analysis = new AnalysisTab(registry)
let debug = new DebuggerTab() let debug = new DebuggerTab()
let test = new TestTab( let test = new TestTab(
registry.get('filemanager').api, registry.get('filemanager').api,
registry.get('filepanel').api, filePanel,
compileTab, compileTab,
appStore appManager
) )
let sourceHighlighters = registry.get('editor').api.sourceHighlighters
appManager.init([
landingPage.api(),
udapp.api(),
fileManager.api(),
sourceHighlighters.api(),
filePanel.api(),
settings.api(),
pluginManagerComponent.api(),
networkModule.api(),
themeModule.api()
])
appManager.registerMany([ appManager.register([
compileTab.api(), compileTab,
run.api(), run,
debug.api(), debug,
analysis.api(), analysis,
test.api(), test,
filePanel.remixdHandle.api(), filePanel.remixdHandle,
...appManager.plugins() ...appManager.registeredPlugins()
]) ])
// Set workspace after initial activation // Set workspace after initial activation
if (Array.isArray(workspace)) { if (Array.isArray(workspace)) appManager.activate(workspace)
appManager.activateMany(workspace)
}
framingService.start(appStore, sidePanel, verticalIcons, mainPanel, this._components.resizeFeature)
// The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event.
let filesToLoad = null
let loadFilesCallback = function (files) { filesToLoad = files } // will be replaced later
window.addEventListener('message', function (ev) {
if (typeof ev.data === typeof [] && ev.data[0] === 'loadFiles') {
loadFilesCallback(ev.data[1])
}
}, false)
// Replace early callback with instant response // Load and start the service who manager layout and frame
loadFilesCallback = function (files) { const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature)
self.loadFiles(files) framingService.start()
}
// Run if we did receive an event from remote instance while starting up
if (filesToLoad !== null) {
self.loadFiles(filesToLoad)
}
const txLogger = new TxLogger() // eslint-disable-line // get the file list from the parent iframe
txLogger.event.register('debuggingRequested', (hash) => { loadFileFromParent(fileManager)
if (!appStore.isActive('debugger')) appManager.activateOne('debugger')
debug.debugger().debug(hash)
verticalIcons.select('debugger')
})
let transactionContextAPI = {
getAddress: (cb) => {
cb(null, $('#txorigin').val())
},
getValue: (cb) => {
try {
var number = document.querySelector('#value').value
var select = document.getElementById('unit')
var index = select.selectedIndex
var selectedUnit = select.querySelectorAll('option')[index].dataset.unit
var unit = 'ether' // default
if (['ether', 'finney', 'gwei', 'wei'].indexOf(selectedUnit) >= 0) {
unit = selectedUnit
}
cb(null, executionContext.web3().toWei(number, unit))
} catch (e) {
cb(e)
}
},
getGasLimit: (cb) => {
cb(null, $('#gasLimit').val())
}
}
udapp.resetAPI(transactionContextAPI)
// get the file from gist
const gistHandler = new GistHandler()
const queryParams = new QueryParams() const queryParams = new QueryParams()
const loadedFromGist = gistHandler.loadFromGist(queryParams.get(), fileManager)
const loadingFromGist = self.loadFromGist(queryParams.get()) if (!loadedFromGist) {
if (!loadingFromGist) {
// insert ballot contract if there are no files to show // insert ballot contract if there are no files to show
self._components.filesProviders['browser'].resolveDirectory('browser', (error, filesList) => { self._components.filesProviders['browser'].resolveDirectory('browser', (error, filesList) => {
if (error) console.error(error) if (error) console.error(error)

@ -0,0 +1,33 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json'
import CompilerAbstract from './compiler-abstract'
const profile = {
name: 'compilerArtefacts',
methods: [],
events: [],
version: packageJson.version,
required: true
}
module.exports = class CompilerArtefacts extends Plugin {
constructor () {
super(profile)
this.compilersArtefacts = {}
}
clear () {
this.compilersArtefacts = {}
}
onActivation () {
this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
this.compilersArtefacts['__last'] = new CompilerAbstract(languageVersion, data, source)
})
this.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => {
this.compilersArtefacts['__last'] = new CompilerAbstract(languageVersion, data, source)
})
}
}

@ -3,9 +3,19 @@ var base64 = require('js-base64').Base64
var swarmgw = require('swarmgw')() var swarmgw = require('swarmgw')()
var resolver = require('@resolver-engine/imports').ImportsEngine() var resolver = require('@resolver-engine/imports').ImportsEngine()
var request = require('request') var request = require('request')
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json'
module.exports = class CompilerImports { const profile = {
name: 'contentImport',
displayName: 'content import',
version: packageJson.version,
methods: ['resolve']
}
module.exports = class CompilerImports extends Plugin {
constructor (githubAccessToken) { constructor (githubAccessToken) {
super(profile)
this.githubAccessToken = githubAccessToken || (() => {}) this.githubAccessToken = githubAccessToken || (() => {})
this.previouslyHandled = {} // cache import so we don't make the request at each compilation. this.previouslyHandled = {} // cache import so we don't make the request at each compilation.
} }
@ -90,7 +100,18 @@ module.exports = class CompilerImports {
return /^([^/]+)/.exec(url) return /^([^/]+)/.exec(url)
} }
resolve (url) {
return new Promise((resolve, reject) => {
this.import(url, null, (error, content, cleanUrl, type, url) => {
if (error) return reject(error)
resolve({ content, cleanUrl, type, url })
})
})
}
import (url, loadingCb, cb) { import (url, loadingCb, cb) {
if (!loadingCb) loadingCb = () => {}
var self = this var self = this
var imported = this.previouslyHandled[url] var imported = this.previouslyHandled[url]
if (imported) { if (imported) {

@ -1,4 +1,5 @@
import { AbstractPanel } from './panel' import { AbstractPanel } from './panel'
import * as packageJson from '../../../package.json'
const csjs = require('csjs-inject') const csjs = require('csjs-inject')
const yo = require('yo-yo') const yo = require('yo-yo')
@ -8,10 +9,18 @@ const css = csjs`
} }
` `
const profile = {
name: 'hiddenPanel',
displayName: 'Hidden Panel',
description: '',
version: packageJson.version,
required: true
}
export class HiddenPanel extends AbstractPanel { export class HiddenPanel extends AbstractPanel {
constructor (appStore) { constructor () {
super('hiddenPanel', appStore) super(profile)
} }
render () { render () {

@ -1,4 +1,5 @@
import { AbstractPanel } from './panel' import { AbstractPanel } from './panel'
import * as packageJson from '../../../package.json'
const yo = require('yo-yo') const yo = require('yo-yo')
const csjs = require('csjs-inject') const csjs = require('csjs-inject')
@ -10,9 +11,18 @@ const css = csjs`
} }
` `
const profile = {
name: 'mainPanel',
displayName: 'Main Panel',
description: '',
version: packageJson.version,
methods: ['addView', 'removeView'],
required: true
}
export class MainPanel extends AbstractPanel { export class MainPanel extends AbstractPanel {
constructor (appStore, options) { constructor (options) {
super('mainPanel', appStore, options) super(profile, options)
} }
render () { render () {

@ -1,7 +1,7 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
const registry = require('../../global/registry')
const csjs = require('csjs-inject') const csjs = require('csjs-inject')
const yo = require('yo-yo') const yo = require('yo-yo')
const { HostPlugin } = require('@remixproject/engine')
const css = csjs` const css = csjs`
.plugins { .plugins {
@ -25,50 +25,17 @@ const css = csjs`
} }
` `
// Events are : 'toggle' | 'showing'
/** Abstract class used for hosting the view of a plugin */ /** Abstract class used for hosting the view of a plugin */
export class AbstractPanel { export class AbstractPanel extends HostPlugin {
constructor (panelName, appStore, opts) { constructor (profile, opts) {
super(profile)
this.events = new EventEmitter() this.events = new EventEmitter()
this.contents = {} this.contents = {}
this.active = undefined this.active = undefined
// View where the plugin HTMLElement leaves // View where the plugin HTMLElement leaves
this.view = yo`<div id="plugins" class="${css.plugins}"></div>` this.view = yo`<div id="plugins" class="${css.plugins}"></div>`
appStore.event.on('activate', (name) => {
const api = appStore.getOne(name)
const profile = api.profile
if (profile.location !== panelName) return
if (!profile.location && !opts.default) return
if (profile.icon && api.render && typeof api.render === 'function') {
this.add(name, api.render())
}
})
appStore.event.on('deactivate', (name) => {
if (this.contents[name]) this.remove(name)
})
const verticalIcon = registry.get('verticalicon').api
// Toggle content
verticalIcon.events.on('toggleContent', (name) => {
if (!this.contents[name]) return
if (this.active === name) {
this.events.emit('toggle', name)
return
}
this.showContent(name)
this.events.emit('showing', name)
})
// Force opening
verticalIcon.events.on('showContent', (name) => {
if (!this.contents[name]) return
this.showContent(name)
this.events.emit('showing', name)
})
} }
/** /**
@ -76,15 +43,25 @@ export class AbstractPanel {
* @param {String} name the name of the plugin * @param {String} name the name of the plugin
* @param {HTMLElement} content the HTMLContent of the plugin * @param {HTMLElement} content the HTMLContent of the plugin
*/ */
add (name, content) { add (view, name) {
console.log('panel', name, view)
if (this.contents[name]) throw new Error(`Plugin ${name} already rendered`) if (this.contents[name]) throw new Error(`Plugin ${name} already rendered`)
content.style.height = '100%' view.style.height = '100%'
content.style.width = '100%' view.style.width = '100%'
content.style.border = '0' view.style.border = '0'
this.contents[name] = yo`<div class="${css.plugItIn}" >${content}</div>` this.contents[name] = yo`<div class="${css.plugItIn}" >${view}</div>`
this.contents[name].style.display = 'none'
this.view.appendChild(this.contents[name]) this.view.appendChild(this.contents[name])
} }
addView (profile, view) {
this.add(view, profile.name)
}
removeView (profile) {
this.remove(profile.name)
}
/** /**
* Remove a plugin from the panel * Remove a plugin from the panel
* @param {String} name The name of the plugin to remove * @param {String} name The name of the plugin to remove
@ -101,6 +78,7 @@ export class AbstractPanel {
* @param {String} name The name of the plugin to display the content * @param {String} name The name of the plugin to display the content
*/ */
showContent (name) { showContent (name) {
console.log('showContent', name, this.active)
if (!this.contents[name]) throw new Error(`Plugin ${name} is not yet activated`) if (!this.contents[name]) throw new Error(`Plugin ${name} is not yet activated`)
// hiding the current view and display the `moduleName` // hiding the current view and display the `moduleName`
if (this.active) { if (this.active) {
@ -109,5 +87,9 @@ export class AbstractPanel {
this.contents[name].style.display = 'block' this.contents[name].style.display = 'block'
this.active = name this.active = name
} }
focus (name) {
this.showContent(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, BaseApi } from 'remix-plugin' import { ViewPlugin, IframePlugin } from '@remixproject/engine'
import { PluginManagerSettings } from './plugin-manager-settings' import { PluginManagerSettings } from './plugin-manager-settings'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
const addToolTip = require('../ui/tooltip') const addToolTip = require('../ui/tooltip')
@ -59,38 +59,31 @@ const profile = {
kind: 'settings', kind: 'settings',
location: 'sidePanel', location: 'sidePanel',
documentation: 'https://remix-ide.readthedocs.io/en/latest/plugin_manager.html', documentation: 'https://remix-ide.readthedocs.io/en/latest/plugin_manager.html',
version: packageJson.version version: packageJson.version,
required: true
} }
class PluginManagerComponent extends BaseApi { class PluginManagerComponent extends ViewPlugin {
constructor () { constructor (appManager) {
super(profile) super(profile)
this.event = new EventEmitter() this.event = new EventEmitter()
this.appManager = appManager
this.views = { this.views = {
root: null, root: null,
items: {} items: {}
} }
this.localPlugin = new LocalPlugin() this.localPlugin = new LocalPlugin()
this.filter = '' this.filter = ''
} this.appManager.event.on('activate', () => { this.reRender() })
this.appManager.event.on('deactivate', () => { this.reRender() })
setApp (appManager) { this.appManager.event.on('added', () => { this.reRender() })
this.appManager = appManager
}
setStore (store) {
this.store = store
this.store.event.on('activate', (name) => { this.reRender() })
this.store.event.on('deactivate', (name) => { this.reRender() })
this.store.event.on('add', (api) => { this.reRender() })
this.store.event.on('remove', (api) => { this.reRender() })
} }
renderItem (name) { renderItem (name) {
const api = this.store.getOne(name) const api = this.appManager.getOne(name)
if (!api) return if (!api) return
const isActive = this.store.actives.includes(name) const isActive = this.appManager.isActive(name)
const displayName = (api.profile.displayName) ? api.profile.displayName : name const displayName = (api.profile.displayName) ? api.profile.displayName : name
// Check version of the plugin // Check version of the plugin
@ -136,12 +129,12 @@ class PluginManagerComponent extends BaseApi {
*/ */
async openLocalPlugin () { async openLocalPlugin () {
try { try {
const profile = await this.localPlugin.open(this.store.getAll()) const profile = await this.localPlugin.open(this.appManager.getAll())
if (!profile) return if (!profile) return
if (this.store.ids.includes(profile.name)) { if (this.appManager.getIds().includes(profile.name)) {
throw new Error('This name has already been used') throw new Error('This name has already been used')
} }
this.appManager.registerOne(new Plugin(profile)) this.appManager.registerOne(new IframePlugin(profile))
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
@ -161,12 +154,12 @@ class PluginManagerComponent extends BaseApi {
} }
// Filter all active and inactive modules that are not required // Filter all active and inactive modules that are not required
const { actives, inactives } = this.store.getAll() const { actives, inactives } = this.appManager.getAll()
.filter(isFiltered) .filter(isFiltered)
.filter(isNotRequired) .filter(isNotRequired)
.sort(sortByName) .sort(sortByName)
.reduce(({actives, inactives}, api) => { .reduce(({actives, inactives}, api) => {
return this.store.actives.includes(api.name) return this.appManager.isActive(api.name)
? { actives: [...actives, api.name], inactives } ? { actives: [...actives, api.name], inactives }
: { inactives: [...inactives, api.name], actives } : { inactives: [...inactives, api.name], actives }
}, { actives: [], inactives: [] }) }, { actives: [], inactives: [] })

@ -1,36 +0,0 @@
var registry = require('../../global/registry')
const CompilerAbstract = require('../compiler/compiler-abstract')
const EventManager = require('remix-lib').EventManager
class PluginManagerProxy {
constructor () {
this.event = new EventManager()
this._listeners = {}
this._listeners['vyper'] = (file, source, languageVersion, data) => {
registry.get('compilersartefacts').api['__last'] = new CompilerAbstract(languageVersion, data, source)
this.event.trigger('sendCompilationResult', [file, source, languageVersion, data])
}
this._listeners['solidity'] = (file, source, languageVersion, data) => {
registry.get('compilersartefacts').api['__last'] = new CompilerAbstract(languageVersion, data, source)
this.event.trigger('sendCompilationResult', [file, source, languageVersion, data])
}
}
register (name, instance) {
if (this._listeners[name]) {
instance.events.on('compilationFinished', this._listeners[name])
}
}
unregister (name, instance) {
if (this._listeners[name]) {
instance.events.removeListener('compilationFinished', this._listeners[name])
}
}
}
module.exports = PluginManagerProxy

@ -1,4 +1,5 @@
import { AbstractPanel } from './panel' import { AbstractPanel } from './panel'
import * as packageJson from '../../../package.json'
const csjs = require('csjs-inject') const csjs = require('csjs-inject')
const yo = require('yo-yo') const yo = require('yo-yo')
@ -49,12 +50,49 @@ const options = {
default: true default: true
} }
const sidePanel = {
name: 'sidePanel',
displayName: 'Side Panel',
description: '',
version: packageJson.version,
methods: ['addView', 'removeView'],
required: true
}
export class SidePanel extends AbstractPanel { export class SidePanel extends AbstractPanel {
constructor (appStore) { constructor (appManager, verticalIcons) {
super('sidePanel', appStore, options) super(sidePanel, options)
this.appManager = appManager
this.header = this.renderHeader() this.header = this.renderHeader()
this.store = appStore this.verticalIcons = verticalIcons
// Toggle content
verticalIcons.events.on('toggleContent', (name) => {
if (!this.contents[name]) return
if (this.active === name) {
this.events.emit('toggle', name)
return
}
this.showContent(name)
this.events.emit('showing', name)
})
// Force opening
verticalIcons.events.on('showContent', (name) => {
if (!this.contents[name]) return
this.showContent(name)
this.events.emit('showing', name)
})
}
removeView (profile) {
super.removeView(profile)
this.verticalIcons.unlinkContent(profile)
}
addView (profile, view) {
super.addView(profile, view)
this.verticalIcons.linkContent(profile)
} }
/** /**
@ -72,7 +110,7 @@ export class SidePanel extends AbstractPanel {
let docLink = '' let docLink = ''
let versionWarning let versionWarning
if (this.active) { if (this.active) {
const { profile } = this.store.getOne(this.active) const { profile } = this.appManager.getOne(this.active)
name = profile.displayName ? profile.displayName : profile.name name = profile.displayName ? profile.displayName : profile.name
docLink = profile.documentation ? yo`<a href="${profile.documentation}" class="${css.titleInfo}" title="link to documentation" target="_blank"><i aria-hidden="true" class="fas fa-book"></i></a>` : '' docLink = profile.documentation ? yo`<a href="${profile.documentation}" class="${css.titleInfo}" title="link to documentation" target="_blank"><i aria-hidden="true" class="fas fa-book"></i></a>` : ''
if (profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) { if (profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) {

@ -2,38 +2,30 @@ var yo = require('yo-yo')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var helper = require('../../lib/helper') var helper = require('../../lib/helper')
let globalRegistry = require('../../global/registry') let globalRegistry = require('../../global/registry')
const { Plugin } = require('@remixproject/engine')
import * as packageJson from '../../../package.json'
const EventEmitter = require('events') const EventEmitter = require('events')
const profile = {
name: 'menuicons',
displayName: 'Vertical Icons',
description: '',
version: packageJson.version,
methods: ['select'],
required: true
}
// Component // Component
export class VerticalIcons { export class VerticalIcons extends Plugin {
constructor (name, appStore, homeProfile) { constructor (appManager) {
this.store = appStore super(profile)
this.homeProfile = homeProfile.profile
this.events = new EventEmitter() this.events = new EventEmitter()
this.appManager = appManager
this.icons = {} this.icons = {}
this.iconKind = {} this.iconKind = {}
this.iconStatus = {} this.iconStatus = {}
this.name = name
this.store.event.on('activate', (name) => {
const api = this.store.getOne(name)
if (!api.profile.icon) return
if (api.profile.location === this.name) {
this.addIcon(api.profile)
this.listenOnStatus(api)
}
})
this.store.event.on('deactivate', (name) => {
const api = this.store.getOne(name)
if (api && this.icons[name]) {
this.removeIcon(api.profile)
this.stopListenOnStatus(api)
}
})
this.store.event.on('add', (api) => { })
this.store.event.on('remove', (api) => { })
let themeModule = globalRegistry.get('themeModule').api let themeModule = globalRegistry.get('themeModule').api
themeModule.events.on('themeChanged', (theme) => { themeModule.events.on('themeChanged', (theme) => {
@ -41,18 +33,17 @@ export class VerticalIcons {
}) })
} }
stopListenOnStatus (api) { linkContent (profile) {
if (!api.events) return if (!profile.icon) return
let fn = this.iconStatus[api.profile.name] this.addIcon(profile)
if (fn) { this.listenOnStatus(profile)
api.events.removeListener('statusChanged', fn)
delete this.iconStatus[api.profile.name]
}
} }
listenOnStatus (api) { unlinkContent (profile) {
if (!api.events) return this.removeIcon(profile)
}
listenOnStatus (profile) {
// the list of supported keys. 'none' will remove the status // the list of supported keys. 'none' will remove the status
const keys = ['edited', 'succeed', 'none', 'loading', 'failed'] const keys = ['edited', 'succeed', 'none', 'loading', 'failed']
const types = ['error', 'warning', 'success', 'info', ''] const types = ['error', 'warning', 'success', 'info', '']
@ -63,10 +54,10 @@ export class VerticalIcons {
if (typeof status.key === 'string' && (!keys.includes(status.key))) { if (typeof status.key === 'string' && (!keys.includes(status.key))) {
throw new Error('key should contain either number or ' + keys.join()) throw new Error('key should contain either number or ' + keys.join())
} }
this.setIconStatus(api.profile.name, status) this.setIconStatus(profile.name, status)
} }
this.iconStatus[api.profile.name] = fn this.iconStatus[profile.name] = fn
api.events.on('statusChanged', this.iconStatus[api.profile.name]) this.on(profile.name, 'statusChanged', this.iconStatus[profile.name])
} }
/** /**
@ -223,21 +214,14 @@ export class VerticalIcons {
} }
} }
/**
* Show the home page
*/
showHome () {
globalRegistry.get('appmanager').api.ensureActivated('home')
}
render () { render () {
let home = yo` let home = yo`
<div <div
class="${css.homeIcon}" class="${css.homeIcon}"
onclick="${(e) => { onclick="${(e) => {
this.showHome() this.appManager.ensureActivated('home')
}}" }}"
plugin="${this.homeProfile.name}" title="${this.homeProfile.displayName}" plugin="home" title="Home"
> >
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="42px" height="42px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve"> width="42px" height="42px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">

@ -1,20 +1,22 @@
'use strict' 'use strict'
const SourceHighlighter = require('./sourceHighlighter') const SourceHighlighter = require('./sourceHighlighter')
import { EditorApi } from 'remix-plugin' import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
const profile = { const profile = {
displayName: 'source highlighters', displayName: 'source highlighters',
name: 'editor', name: 'sourceHighlighters',
description: 'service - highlight source code', description: 'service - highlight source code',
version: packageJson.version version: packageJson.version,
methods: ['highlight', 'discardHighlight'],
required: true
} }
// EditorApi: // EditorApi:
// - methods: ['highlight', 'discardHighlight'], // - methods: ['highlight', 'discardHighlight'],
class SourceHighlighters extends EditorApi { class SourceHighlighters extends Plugin {
constructor () { constructor () {
super(profile) super(profile)

@ -5,15 +5,26 @@ const SourceMappingDecoder = remixLib.SourceMappingDecoder
const AstWalker = remixLib.AstWalker const AstWalker = remixLib.AstWalker
const EventManager = require('../../lib/events') const EventManager = require('../../lib/events')
const globalRegistry = require('../../global/registry') const globalRegistry = require('../../global/registry')
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json'
const profile = {
name: 'contextualListener',
methods: [],
events: [],
version: packageJson.version,
required: true
}
/* /*
trigger contextChanged(nodes) trigger contextChanged(nodes)
*/ */
class ContextualListener { class ContextualListener extends Plugin {
constructor (opts, localRegistry) { constructor (opts) {
super(profile)
this.event = new EventManager() this.event = new EventManager()
this._components = {} this._components = {}
this._components.registry = localRegistry || globalRegistry this._components.registry = globalRegistry
this.editor = opts.editor this.editor = opts.editor
this.pluginManager = opts.pluginManager this.pluginManager = opts.pluginManager
this._deps = { this._deps = {
@ -26,8 +37,14 @@ class ContextualListener {
FlatReferences: {} FlatReferences: {}
} }
this._activeHighlights = [] this._activeHighlights = []
this.editor.event.register('contentChanged', () => { this._stopHighlighting() })
this.sourceMappingDecoder = new SourceMappingDecoder()
this.astWalker = new AstWalker()
}
this.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { onActivation () {
this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
if (languageVersion.indexOf('soljson') !== 0) return if (languageVersion.indexOf('soljson') !== 0) return
this._stopHighlighting() this._stopHighlighting()
this._index = { this._index = {
@ -37,10 +54,6 @@ class ContextualListener {
this._buildIndex(data, source) this._buildIndex(data, source)
}) })
this.editor.event.register('contentChanged', () => { this._stopHighlighting() })
this.sourceMappingDecoder = new SourceMappingDecoder()
this.astWalker = new AstWalker()
setInterval(() => { setInterval(() => {
if (this._deps.compilersArtefacts['__last'] && this._deps.compilersArtefacts['__last'].languageversion.indexOf('soljson') === 0) { if (this._deps.compilersArtefacts['__last'] && this._deps.compilersArtefacts['__last'].languageversion.indexOf('soljson') === 0) {
this._highlightItems(this.editor.getCursorPosition(), this._deps.compilersArtefacts['__last'], this._deps.config.get('currentFile')) this._highlightItems(this.editor.getCursorPosition(), this._deps.compilersArtefacts['__last'], this._deps.config.get('currentFile'))

@ -38,13 +38,11 @@ document.head.appendChild(yo`
class Editor { class Editor {
constructor (opts = {}, localRegistry) { constructor (opts = {}, themeModule) {
// Dependancies // Dependancies
this._components = {} this._components = {}
this._components.registry = localRegistry || globalRegistry this._components.registry = globalRegistry
this._deps = { this._deps = {
themeModule: this._components.registry.get('themeModule').api,
fileManager: this._components.registry.get('filemanager').api,
config: this._components.registry.get('config').api config: this._components.registry.get('config').api
} }
@ -52,7 +50,7 @@ class Editor {
'light': 'chrome', 'light': 'chrome',
'dark': 'chaos' 'dark': 'chaos'
} }
this._deps.themeModule.events.on('themeChanged', (theme) => { themeModule.events.on('themeChanged', (theme) => {
this.setTheme(theme.quality) this.setTheme(theme.quality)
}) })
@ -202,7 +200,7 @@ class Editor {
window.clearTimeout(this.saveTimeout) window.clearTimeout(this.saveTimeout)
} }
this.saveTimeout = window.setTimeout(() => { this.saveTimeout = window.setTimeout(() => {
this._deps.fileManager.saveCurrentFile() this.event.trigger('requiringToSaveCurrentfile', [])
}, 5000) }, 5000)
} }

@ -2,16 +2,8 @@
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
import { BaseApi } from 'remix-plugin' class FilesTree {
class FilesTree extends BaseApi {
constructor (name, storage) { constructor (name, storage) {
super({
name: name,
methods: ['get', 'set', 'remove'],
description:
'service - read/write file to the `config` explorer without need of additionnal permission.'
})
this.event = new EventManager() this.event = new EventManager()
this.storage = storage this.storage = storage
this.type = name this.type = name

@ -1,21 +1,33 @@
'use strict' 'use strict'
var executionContext = require('../../execution-context') var executionContext = require('../../execution-context')
var CompilerAbstract = require('../compiler/compiler-abstract') var CompilerAbstract = require('../compiler/compiler-abstract')
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json'
class CompilerMetadata { const profile = {
constructor (opts) { name: 'compilerMetadata',
methods: ['deployMetadataOf'],
events: [],
version: packageJson.version,
required: true
}
class CompilerMetadata extends Plugin {
constructor (fileManager, config) {
super(profile)
var self = this var self = this
self._opts = opts self.fileManager = fileManager
self.config = config
self.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'görli:5', 'Custom'] self.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'görli:5', 'Custom']
} }
syncContractMetadata () { onActivation () {
var self = this var self = this
self._opts.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
if (!self._opts.config.get('settings/generate-contract-metadata')) return if (!self.config.get('settings/generate-contract-metadata')) return
let compiler = new CompilerAbstract(languageVersion, data, source) let compiler = new CompilerAbstract(languageVersion, data, source)
var provider = self._opts.fileManager.currentFileProvider() var provider = self.fileManager.currentFileProvider()
var path = self._opts.fileManager.currentPath() var path = self.fileManager.currentPath()
if (provider && path) { if (provider && path) {
compiler.visitContracts((contract) => { compiler.visitContracts((contract) => {
if (contract.file !== source.target) return if (contract.file !== source.target) return
@ -75,30 +87,32 @@ class CompilerMetadata {
} }
// TODO: is only called by dropdownLogic and can be moved there // TODO: is only called by dropdownLogic and can be moved there
deployMetadataOf (contractName, callback) { deployMetadataOf (contractName) {
var self = this return new Promise((resolve, reject) => {
var provider = self._opts.fileManager.currentFileProvider() var self = this
var path = self._opts.fileManager.currentPath() var provider = self.fileManager.currentFileProvider()
if (provider && path) { var path = self.fileManager.currentPath()
executionContext.detectNetwork((err, { id, name } = {}) => { if (provider && path) {
if (err) { executionContext.detectNetwork((err, { id, name } = {}) => {
console.log(err) if (err) {
} else { console.log(err)
var fileName = path + '/' + contractName + '.json' } else {
provider.get(fileName, (error, content) => { var fileName = path + '/' + contractName + '.json'
if (error) return callback(error) provider.get(fileName, (error, content) => {
if (!content) return callback() if (error) return reject(error)
try { if (!content) return resolve()
var metadata = JSON.parse(content) try {
metadata = metadata.deploy || {} var metadata = JSON.parse(content)
return callback(null, metadata[name + ':' + id] || metadata[name] || metadata[id] || metadata[name.toLowerCase() + ':' + id] || metadata[name.toLowerCase()]) metadata = metadata.deploy || {}
} catch (e) { return resolve(metadata[name + ':' + id] || metadata[name] || metadata[id] || metadata[name.toLowerCase() + ':' + id] || metadata[name.toLowerCase()])
callback(e.message) } catch (e) {
} reject(e.message)
}) }
} })
}) }
} })
}
})
} }
} }

@ -1,12 +1,14 @@
'use strict' 'use strict'
import yo from 'yo-yo' import yo from 'yo-yo'
import async from 'async'
const EventEmitter = require('events') const EventEmitter = require('events')
const globalRegistry = require('../../global/registry') const globalRegistry = require('../../global/registry')
const CompilerImport = require('../compiler/compiler-imports') const CompilerImport = require('../compiler/compiler-imports')
const toaster = require('../ui/tooltip') const toaster = require('../ui/tooltip')
const modalDialogCustom = require('../ui/modal-dialog-custom')
const helper = require('../../lib/helper.js') const helper = require('../../lib/helper.js')
import { FileSystemApi } from 'remix-plugin' import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
/* /*
@ -20,26 +22,28 @@ const profile = {
description: 'Service - read/write to any files or folders, require giving permissions', description: 'Service - read/write to any files or folders, require giving permissions',
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4=', icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4=',
permission: true, permission: true,
version: packageJson.version version: packageJson.version,
methods: ['getFolder', 'getCurrentFile', 'getFile', 'setFile', 'switchFile'],
required: true
} }
// File System profile // File System profile
// - events: ['currentFileChanged']
// - methods: ['getFolder', 'getCurrentFile', 'getFile', 'setFile', 'switchFile'] // - methods: ['getFolder', 'getCurrentFile', 'getFile', 'setFile', 'switchFile']
class FileManager extends FileSystemApi { class FileManager extends Plugin {
constructor (localRegistry) { constructor (editor) {
super(profile) super(profile)
this.openedFiles = {} // list all opened files this.openedFiles = {} // list all opened files
this.events = new EventEmitter() this.events = new EventEmitter()
this.editor = editor
this._components = {} this._components = {}
this._components.compilerImport = new CompilerImport() this._components.compilerImport = new CompilerImport()
this._components.registry = localRegistry || globalRegistry this._components.registry = globalRegistry
this.init()
} }
init () { init () {
this._deps = { this._deps = {
editor: this._components.registry.get('editor').api,
config: this._components.registry.get('config').api, config: this._components.registry.get('config').api,
browserExplorer: this._components.registry.get('fileproviders/browser').api, browserExplorer: this._components.registry.get('fileproviders/browser').api,
localhostExplorer: this._components.registry.get('fileproviders/localhost').api, localhostExplorer: this._components.registry.get('fileproviders/localhost').api,
@ -60,7 +64,7 @@ class FileManager extends FileSystemApi {
fileRenamedEvent (oldName, newName, isFolder) { fileRenamedEvent (oldName, newName, isFolder) {
if (!isFolder) { if (!isFolder) {
this._deps.config.set('currentFile', '') this._deps.config.set('currentFile', '')
this._deps.editor.discard(oldName) this.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
@ -102,7 +106,7 @@ class FileManager extends FileSystemApi {
if (Object.keys(this.openedFiles).length) { if (Object.keys(this.openedFiles).length) {
this.switchFile(Object.keys(this.openedFiles)[0]) this.switchFile(Object.keys(this.openedFiles)[0])
} else { } else {
this._deps.editor.displayEmptyReadOnlySession() this.editor.displayEmptyReadOnlySession()
this._deps.config.set('currentFile', '') this._deps.config.set('currentFile', '')
this.events.emit('noFileSelected') this.events.emit('noFileSelected')
} }
@ -127,6 +131,7 @@ class FileManager extends FileSystemApi {
if (!provider) throw new Error(`${path} not available`) if (!provider) throw new Error(`${path} not available`)
// TODO: change provider to Promise // TODO: change provider to Promise
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (this.currentFile() === path) return resolve(this.editor.currentContent())
provider.get(path, (err, content) => { provider.get(path, (err, content) => {
if (err) reject(err) if (err) reject(err)
resolve(content) resolve(content)
@ -219,7 +224,7 @@ class FileManager extends FileSystemApi {
if (path === this._deps.config.get('currentFile')) { if (path === this._deps.config.get('currentFile')) {
this._deps.config.set('currentFile', '') this._deps.config.set('currentFile', '')
} }
this._deps.editor.discard(path) this.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()
@ -241,9 +246,9 @@ class FileManager extends FileSystemApi {
console.log(error) console.log(error)
} else { } else {
if (this.fileProviderOf(file).isReadOnly(file)) { if (this.fileProviderOf(file).isReadOnly(file)) {
this._deps.editor.openReadOnly(file, content) this.editor.openReadOnly(file, content)
} else { } else {
this._deps.editor.open(file, content) this.editor.open(file, content)
} }
this.events.emit('currentFileChanged', file) this.events.emit('currentFileChanged', file)
} }
@ -258,7 +263,7 @@ class FileManager extends FileSystemApi {
if (fileList.length) { if (fileList.length) {
_switchFile(browserProvider.type + '/' + fileList[0]) _switchFile(browserProvider.type + '/' + fileList[0])
} else { } else {
this._deps.editor.displayEmptyReadOnlySession() this.editor.displayEmptyReadOnlySession()
this.events.emit('noFileSelected') this.events.emit('noFileSelected')
} }
}) })
@ -294,8 +299,8 @@ class FileManager extends FileSystemApi {
saveCurrentFile () { saveCurrentFile () {
var currentFile = this._deps.config.get('currentFile') var currentFile = this._deps.config.get('currentFile')
if (currentFile && this._deps.editor.current()) { if (currentFile && this.editor.current()) {
var input = this._deps.editor.get(currentFile) var input = this.editor.get(currentFile)
if (input) { if (input) {
var provider = this.fileProviderOf(currentFile) var provider = this.fileProviderOf(currentFile)
if (provider) { if (provider) {
@ -315,12 +320,34 @@ class FileManager extends FileSystemApi {
if (provider) { if (provider) {
provider.get(currentFile, (error, content) => { provider.get(currentFile, (error, content) => {
if (error) console.log(error) if (error) console.log(error)
this._deps.editor.setText(content) this.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')
} }
} }
setBatchFiles (filesSet, fileProvider, callback) {
const self = this
if (!fileProvider) fileProvider = 'browser'
async.each(Object.keys(filesSet), (file, callback) => {
helper.createNonClashingName(file, self._deps.filesProviders[fileProvider],
(error, name) => {
if (error) {
modalDialogCustom.alert('Unexpected error loading the file ' + error)
} else if (helper.checkSpecialChars(name)) {
modalDialogCustom.alert('Special characters are not allowed')
} else {
self._deps.filesProviders[fileProvider].set(name, filesSet[file].content)
}
callback()
})
}, (error) => {
if (!error) self.switchFile()
if (callback) callback(error)
})
}
} }
module.exports = FileManager module.exports = FileManager

@ -1,5 +1,4 @@
let globalRegistry = require('../../global/registry') import { Plugin } from '@remixproject/engine'
import { BaseApi } from 'remix-plugin'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
var yo = require('yo-yo') var yo = require('yo-yo')
var modalDialog = require('../ui/modaldialog') var modalDialog = require('../ui/modaldialog')
@ -27,11 +26,12 @@ const profile = {
version: packageJson.version version: packageJson.version
} }
export class RemixdHandle extends BaseApi { export class RemixdHandle extends Plugin {
constructor (fileSystemExplorer, locahostProvider) { constructor (fileSystemExplorer, locahostProvider, appManager) {
super(profile) super(profile)
this.fileSystemExplorer = fileSystemExplorer this.fileSystemExplorer = fileSystemExplorer
this.locahostProvider = locahostProvider this.locahostProvider = locahostProvider
this.appManager = appManager
} }
deactivate () { deactivate () {
@ -45,8 +45,7 @@ export class RemixdHandle extends BaseApi {
} }
canceled () { canceled () {
let appManager = globalRegistry.get('appmanager').api this.appManager.ensureDeactivated('remixd')
appManager.ensureDeactivated('remixd')
} }
/** /**

@ -1,12 +1,11 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var CompilerMetadata = require('../files/compiler-metadata')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var FileExplorer = require('../files/file-explorer') var FileExplorer = require('../files/file-explorer')
var { RemixdHandle } = require('../files/remixd-handle.js') var { RemixdHandle } = require('../files/remixd-handle.js')
var globalRegistry = require('../../global/registry') var globalRegistry = require('../../global/registry')
var css = require('./styles/file-panel-styles') var css = require('./styles/file-panel-styles')
import { ViewPlugin } from '@remixproject/engine'
import { BaseApi } from 'remix-plugin'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
var canUpload = window.File || window.FileReader || window.FileList || window.Blob var canUpload = window.File || window.FileReader || window.FileList || window.Blob
@ -38,21 +37,21 @@ const profile = {
kind: 'fileexplorer', kind: 'fileexplorer',
location: 'sidePanel', location: 'sidePanel',
documentation: 'https://remix-ide.readthedocs.io/en/latest/file_explorer.html', documentation: 'https://remix-ide.readthedocs.io/en/latest/file_explorer.html',
version: packageJson.version version: packageJson.version,
required: true
} }
module.exports = class Filepanel extends BaseApi { module.exports = class Filepanel extends ViewPlugin {
constructor (localRegistry) { constructor (appManager) {
super(profile) super(profile)
var self = this var self = this
self._components = {} self._components = {}
self._components.registry = localRegistry || globalRegistry self._components.registry = globalRegistry
self._deps = { self._deps = {
fileProviders: self._components.registry.get('fileproviders').api, fileProviders: self._components.registry.get('fileproviders').api,
fileManager: self._components.registry.get('filemanager').api, fileManager: self._components.registry.get('filemanager').api,
config: self._components.registry.get('config').api, config: self._components.registry.get('config').api
pluginManager: self._components.registry.get('pluginmanager').api
} }
var fileExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['browser'], var fileExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['browser'],
['createNewFile', 'publishToGist', 'copyFiles', canUpload ? 'uploadFile' : ''] ['createNewFile', 'publishToGist', 'copyFiles', canUpload ? 'uploadFile' : '']
@ -64,20 +63,7 @@ module.exports = class Filepanel extends BaseApi {
var httpExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['http']) var httpExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['http'])
var httpsExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['https']) var httpsExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['https'])
self.remixdHandle = new RemixdHandle(fileSystemExplorer, self._deps.fileProviders['localhost'], self.remixdHandle = new RemixdHandle(fileSystemExplorer, self._deps.fileProviders['localhost'], appManager)
self._deps.fileProviders['localhost'].isReadOnly ? ['createNewFile'] : [])
// ----------------- editor panel ----------------------
self._compilerMetadata = new CompilerMetadata(
{
fileManager: self._deps.fileManager,
pluginManager: self._deps.pluginManager,
config: self._deps.config
}
)
self._compilerMetadata.syncContractMetadata()
self.compilerMetadata = () => { return self._compilerMetadata }
function template () { function template () {
return yo` return yo`

@ -2,7 +2,6 @@ var yo = require('yo-yo')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var Terminal = require('./terminal') var Terminal = require('./terminal')
var Editor = require('../editor/editor')
var globalRegistry = require('../../global/registry') var globalRegistry = require('../../global/registry')
var { TabProxy } = require('./tab-proxy.js') var { TabProxy } = require('./tab-proxy.js')
@ -28,46 +27,43 @@ var css = csjs`
` `
export class MainView { export class MainView {
constructor (appStore, appManager, mainPanel) { constructor (editor, mainPanel, fileManager, appManager, txListener, eventsDecoder) {
var self = this var self = this
self.event = new EventManager() self.event = new EventManager()
self._view = {} self._view = {}
self._components = {} self._components = {}
self._components.registry = globalRegistry self._components.registry = globalRegistry
self._components.editor = new Editor({}) self.editor = editor
self._components.registry.put({api: self._components.editor, name: 'editor'}) self.fileManager = fileManager
self.appStore = appStore
self.appManager = appManager
self.mainPanel = mainPanel self.mainPanel = mainPanel
self.txListener = txListener
self.eventsDecoder = eventsDecoder
this.appManager = appManager
this.init()
}
showApp (name) {
this.fileManager.unselectCurrentFile()
this.mainPanel.showContent(name)
this._view.editor.style.display = 'none'
this._components.contextView.hide()
this._view.mainPanel.style.display = 'block'
}
getAppPanel () {
return this.mainPanel
} }
init () { init () {
var self = this var self = this
self._deps = { self._deps = {
config: self._components.registry.get('config').api, config: self._components.registry.get('config').api,
txListener: self._components.registry.get('txlistener').api, fileManager: self._components.registry.get('filemanager').api
fileManager: self._components.registry.get('filemanager').api,
udapp: self._components.registry.get('udapp').api,
pluginManager: self._components.registry.get('pluginmanager').api
}
self.tabProxy = new TabProxy(self._deps.fileManager, self._components.editor, self.appStore, self.appManager)
let showApp = function (name) {
self._deps.fileManager.unselectCurrentFile()
self.mainPanel.showContent(name)
self._view.editor.style.display = 'none'
self._components.contextView.hide()
self._view.mainPanel.style.display = 'block'
} }
self.appManager.event.on('ensureActivated', (name) => {
if (name === 'home') { self.tabProxy = new TabProxy(self.fileManager, self.editor, self.appManager)
showApp(name)
self.tabProxy.showTab('home')
}
})
/* /*
We listen here on event from the tab component to display / hide the editor and mainpanel We listen here on event from the tab component to display / hide the editor and mainpanel
depending on the content that should be displayed depending on the content that should be displayed
*/ */
self._deps.fileManager.events.on('currentFileChanged', (file) => { self.fileManager.events.on('currentFileChanged', (file) => {
// we check upstream for "fileChanged" // we check upstream for "fileChanged"
self._view.editor.style.display = 'block' self._view.editor.style.display = 'block'
self._view.mainPanel.style.display = 'none' self._view.mainPanel.style.display = 'none'
@ -80,7 +76,7 @@ export class MainView {
}) })
self.tabProxy.event.on('closeFile', (file) => { self.tabProxy.event.on('closeFile', (file) => {
}) })
self.tabProxy.event.on('switchApp', showApp) self.tabProxy.event.on('switchApp', self.showApp.bind(self))
self.tabProxy.event.on('closeApp', (name) => { self.tabProxy.event.on('closeApp', (name) => {
self._view.editor.style.display = 'block' self._view.editor.style.display = 'block'
self._components.contextView.show() self._components.contextView.show()
@ -95,15 +91,19 @@ export class MainView {
} }
} }
var contextualListener = new ContextualListener({editor: self._components.editor, pluginManager: self._deps.pluginManager}) var contextualListener = new ContextualListener({editor: self.editor})
var contextView = new ContextView({contextualListener, editor: self._components.editor}) this.appManager.registerOne(contextualListener)
this.appManager.activate('contextualListener')
var contextView = new ContextView({contextualListener, editor: self.editor})
self._components.contextualListener = contextualListener self._components.contextualListener = contextualListener
self._components.contextView = contextView self._components.contextView = contextView
self._components.terminal = new Terminal({ self._components.terminal = new Terminal({
udapp: self._deps.udapp, appManager: this.appManager,
appStore: self.appStore, eventsDecoder: this.eventsDecoder,
appManager: self.appManager txListener: this.txListener
}, },
{ {
getPosition: (event) => { getPosition: (event) => {
@ -117,9 +117,9 @@ export class MainView {
}) })
self._components.terminal.event.register('resize', delta => self._adjustLayout('top', delta)) self._components.terminal.event.register('resize', delta => self._adjustLayout('top', delta))
if (self._deps.txListener) { if (self.txListener) {
self._components.terminal.event.register('listenOnNetWork', (listenOnNetWork) => { self._components.terminal.event.register('listenOnNetWork', (listenOnNetWork) => {
self._deps.txListener.setListenOnNetwork(listenOnNetWork) self.txListener.setListenOnNetwork(listenOnNetWork)
}) })
} }
} }
@ -148,13 +148,16 @@ export class MainView {
self._view.editor.style.height = `${mainPanelHeight}px` self._view.editor.style.height = `${mainPanelHeight}px`
self._view.mainPanel.style.height = `${mainPanelHeight}px` self._view.mainPanel.style.height = `${mainPanelHeight}px`
self._view.terminal.style.height = `${delta}px` // - menu bar height self._view.terminal.style.height = `${delta}px` // - menu bar height
self._components.editor.resize((document.querySelector('#editorWrap') || {}).checked) self.editor.resize((document.querySelector('#editorWrap') || {}).checked)
self._components.terminal.scroll2bottom() self._components.terminal.scroll2bottom()
} }
} }
getTerminal () {
return this._components.terminal
}
getEditor () { getEditor () {
var self = this var self = this
return self._components.editor return self.editor
} }
refresh () { refresh () {
var self = this var self = this
@ -176,7 +179,7 @@ export class MainView {
render () { render () {
var self = this var self = this
if (self._view.el) return self._view.el if (self._view.el) return self._view.el
self._view.editor = self._components.editor.render() self._view.editor = self.editor.render()
self._view.editor.style.display = 'none' self._view.editor.style.display = 'none'
self._view.mainPanel = self.mainPanel.render() self._view.mainPanel = self.mainPanel.render()
self._view.terminal = self._components.terminal.render() self._view.terminal = self._components.terminal.render()

@ -5,7 +5,7 @@ const EventEmitter = require('events')
require('remix-tabs') require('remix-tabs')
export class TabProxy { export class TabProxy {
constructor (fileManager, editor, appStore, appManager) { constructor (fileManager, editor, appManager) {
this.event = new EventEmitter() this.event = new EventEmitter()
this.fileManager = fileManager this.fileManager = fileManager
this.appManager = appManager this.appManager = appManager
@ -49,8 +49,8 @@ export class TabProxy {
}) })
}) })
appStore.event.on('activate', (name) => { appManager.event.on('activate', (name) => {
const { profile } = appStore.getOne(name) const { profile } = appManager.getOne(name)
if (profile.location === 'mainPanel') { if (profile.location === 'mainPanel') {
this.addTab( this.addTab(
name, name,
@ -66,7 +66,7 @@ export class TabProxy {
} }
}) })
appStore.event.on('deactivate', (name) => { appManager.event.on('deactivate', (name) => {
this.removeTab(name) this.removeTab(name)
}) })
} }

@ -12,11 +12,12 @@ var swarmgw = require('swarmgw')()
var CommandInterpreterAPI = require('../../lib/cmdInterpreterAPI') var CommandInterpreterAPI = require('../../lib/cmdInterpreterAPI')
var executionContext = require('../../execution-context') var executionContext = require('../../execution-context')
var AutoCompletePopup = require('../ui/auto-complete-popup') var AutoCompletePopup = require('../ui/auto-complete-popup')
var TxLogger = require('../../app/ui/txLogger')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var css = require('./styles/terminal-styles') var css = require('./styles/terminal-styles')
import { BaseApi } from 'remix-plugin' import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
var packageV = require('../../../package.json') var packageV = require('../../../package.json')
@ -33,11 +34,11 @@ const profile = {
methods: [], methods: [],
events: [], events: [],
description: ' - ', description: ' - ',
required: false, required: true,
version: packageJson.version version: packageJson.version
} }
class Terminal extends BaseApi { class Terminal extends Plugin {
constructor (opts, api) { constructor (opts, api) {
super(profile) super(profile)
var self = this var self = this
@ -95,14 +96,17 @@ class Terminal extends BaseApi {
self._jsSandboxContext = {} self._jsSandboxContext = {}
self._jsSandboxRegistered = {} self._jsSandboxRegistered = {}
self.externalApi = this.api() // TODO move this to the application start
self.externalApi.notifs = {'theme': ['switchTheme']} opts.appManager.register(this)
opts.appManager.init([self.externalApi]) opts.appManager.activate('terminal')
opts.appManager.activateRequestAndNotification(self.externalApi)
if (opts.shell) self._shell = opts.shell if (opts.shell) self._shell = opts.shell
register(self) register(self)
} }
logHtml (html) {
var command = this.commands['html']
if (typeof command === 'function') command(html)
}
focus () { focus () {
if (this._view.input) this._view.input.focus() if (this._view.input) this._view.input.focus()
} }
@ -194,8 +198,8 @@ class Terminal extends BaseApi {
${self._view.term} ${self._view.term}
</div> </div>
` `
setInterval(() => { setInterval(async () => {
self._view.pendingTxCount.innerHTML = self._opts.udapp.pendingTransactionsCount() self._view.pendingTxCount.innerHTML = await self.call('udapp', 'pendingTransactionsCount')
}, 1000) }, 1000)
function listenOnNetwork (ev) { function listenOnNetwork (ev) {
@ -430,6 +434,15 @@ class Terminal extends BaseApi {
self._shell('remix.help()', self.commands, () => {}) self._shell('remix.help()', self.commands, () => {})
self.commands.html(intro) self.commands.html(intro)
self._components.txLogger = new TxLogger(self._opts.eventsDecoder, self._opts.txListener, this)
self._components.txLogger.event.register('debuggingRequested', (hash) => {
// TODO should probably be in the run module
if (!self._opts.appManager.isActive('debugger')) self._opts.appManager.activateOne('debugger')
this.call('debugger', 'debug', hash)
this.call('menuicons', 'select', 'debugger')
})
return self._view.el return self._view.el
function change (event) { function change (event) {

@ -1,9 +1,9 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var StaticAnalysis = require('../staticanalysis/staticAnalysisView') 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')
import { BaseApi } from 'remix-plugin' import { ViewPlugin } from '@remixproject/engine'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
@ -20,7 +20,7 @@ const profile = {
version: packageJson.version version: packageJson.version
} }
class AnalysisTab extends BaseApi { class AnalysisTab extends ViewPlugin {
constructor (registry) { constructor (registry) {
super(profile) super(profile)
@ -30,15 +30,15 @@ class AnalysisTab extends BaseApi {
} }
render () { render () {
if (!this.staticanalysis) this.staticanalysis = new StaticAnalysis() if (!this.staticanalysis) this.staticanalysis = new StaticAnalysis(this.registry, this)
this.staticanalysis.event.register('staticAnaysisWarning', (count) => { this.staticanalysis.event.register('staticAnaysisWarning', (count) => {
if (count > 0) { if (count > 0) {
this.events.emit('statusChanged', {key: count, title: `${count} warning${count === 1 ? '' : 's'}`, type: 'warning'}) this.emit('statusChanged', {key: count, title: `${count} warning${count === 1 ? '' : 's'}`, type: 'warning'})
} else if (count === 0) { } else if (count === 0) {
this.events.emit('statusChanged', {key: 'succeed', title: 'no warning', type: 'success'}) this.emit('statusChanged', {key: 'succeed', title: 'no warning', type: 'success'})
} else { } else {
// count ==-1 no compilation result // count ==-1 no compilation result
this.events.emit('statusChanged', {key: 'none'}) this.emit('statusChanged', {key: 'none'})
} }
}) })
this.registry.put({api: this.staticanalysis, name: 'staticanalysis'}) this.registry.put({api: this.staticanalysis, name: 'staticanalysis'})

@ -8,8 +8,8 @@ const TreeView = require('../ui/TreeView')
const modalDialog = require('../ui/modaldialog') const modalDialog = require('../ui/modaldialog')
const copyToClipboard = require('../ui/copy-to-clipboard') const copyToClipboard = require('../ui/copy-to-clipboard')
const modalDialogCustom = require('../ui/modal-dialog-custom') const modalDialogCustom = require('../ui/modal-dialog-custom')
const parseContracts = require('../contract/contractParser') const parseContracts = require('./compileTab/contractParser')
const publishOnSwarm = require('../contract/publishOnSwarm') const publishOnSwarm = require('../../lib/publishOnSwarm')
const addTooltip = require('../ui/tooltip') const addTooltip = require('../ui/tooltip')
var css = require('./styles/compile-tab-styles') var css = require('./styles/compile-tab-styles')
@ -17,7 +17,7 @@ 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')
import { CompilerApi } from 'remix-plugin' import { ViewPlugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
const profile = { const profile = {
@ -29,16 +29,18 @@ const profile = {
permission: true, permission: true,
location: 'sidePanel', location: 'sidePanel',
documentation: 'https://remix-ide.readthedocs.io/en/latest/solidity_editor.html', documentation: 'https://remix-ide.readthedocs.io/en/latest/solidity_editor.html',
version: packageJson.version version: packageJson.version,
methods: ['getCompilationResult']
} }
// EditorApi: // EditorApi:
// - events: ['compilationFinished'], // - events: ['compilationFinished'],
// - methods: ['getCompilationResult'] // - methods: ['getCompilationResult']
class CompileTab extends CompilerApi { class CompileTab extends ViewPlugin {
constructor (editor, config, renderer, swarmfileProvider, fileManager, fileProviders, pluginManager) { constructor (editor, config, renderer, swarmfileProvider, fileManager, fileProviders) {
super(profile) super(profile)
this.events = new EventEmitter() this.events = new EventEmitter()
this._view = { this._view = {
@ -56,12 +58,13 @@ class CompileTab extends CompilerApi {
this.swarmfileProvider = swarmfileProvider this.swarmfileProvider = swarmfileProvider
this.fileManager = fileManager this.fileManager = fileManager
this.fileProviders = fileProviders this.fileProviders = fileProviders
this.pluginManager = pluginManager
this.data = { this.data = {
contractsDetails: {} contractsDetails: {}
} }
}
onActivationInternal () {
this.compileTabLogic = new CompileTabLogic(this.queryParams, this.fileManager, this.editor, this.config, this.fileProviders) this.compileTabLogic = new CompileTabLogic(this.queryParams, this.fileManager, this.editor, this.config, this.fileProviders)
this.compiler = this.compileTabLogic.compiler this.compiler = this.compileTabLogic.compiler
this.compileTabLogic.init() this.compileTabLogic.init()
@ -80,23 +83,23 @@ class CompileTab extends CompilerApi {
listenToEvents () { listenToEvents () {
let onContentChanged = () => { let onContentChanged = () => {
this.events.emit('statusChanged', {key: 'edited', title: 'the content has changed, needs recompilation', type: 'info'}) this.emit('statusChanged', {key: 'edited', title: 'the content has changed, needs recompilation', type: 'info'})
} }
this.editor.event.register('contentChanged', onContentChanged) this.editor.event.register('contentChanged', onContentChanged)
this.compiler.event.register('loadingCompiler', () => { this.compiler.event.register('loadingCompiler', () => {
this.events.emit('statusChanged', {key: 'loading', title: 'loading compiler...', type: 'info'}) this.emit('statusChanged', {key: 'loading', title: 'loading compiler...', type: 'info'})
}) })
this.compiler.event.register('compilerLoaded', () => { this.compiler.event.register('compilerLoaded', () => {
this.events.emit('statusChanged', {key: 'none'}) this.emit('statusChanged', {key: 'none'})
}) })
this.compileTabLogic.event.on('startingCompilation', () => { this.compileTabLogic.event.on('startingCompilation', () => {
if (this._view.errorContainer) { if (this._view.errorContainer) {
this._view.errorContainer.innerHTML = '' this._view.errorContainer.innerHTML = ''
} }
this.events.emit('statusChanged', {key: 'loading', title: 'compiling...', type: 'info'}) this.emit('statusChanged', {key: 'loading', title: 'compiling...', type: 'info'})
}) })
this.fileManager.events.on('currentFileChanged', (name) => { this.fileManager.events.on('currentFileChanged', (name) => {
@ -110,14 +113,14 @@ class CompileTab extends CompilerApi {
this.compiler.event.register('compilationFinished', (success, data, source) => { this.compiler.event.register('compilationFinished', (success, data, source) => {
if (success) { if (success) {
// forwarding the event to the appManager infra // forwarding the event to the appManager infra
this.events.emit('compilationFinished', source.target, source, 'soljson', data) this.emit('compilationFinished', source.target, source, 'soljson', data)
if (data.errors) { if (data.errors) {
this.events.emit('statusChanged', { this.emit('statusChanged', {
key: data.errors.length, key: data.errors.length,
title: `compilation finished successful with warning${data.errors.length > 1 ? 's' : ''}`, title: `compilation finished successful with warning${data.errors.length > 1 ? 's' : ''}`,
type: 'warning' type: 'warning'
}) })
} else this.events.emit('statusChanged', {key: 'succeed', title: 'compilation successful', type: 'success'}) } else this.emit('statusChanged', {key: 'succeed', title: 'compilation successful', type: 'success'})
// Store the contracts // Store the contracts
this.data.contractsDetails = {} this.data.contractsDetails = {}
this.compiler.visitContracts((contract) => { this.compiler.visitContracts((contract) => {
@ -129,7 +132,7 @@ class CompileTab extends CompilerApi {
}) })
} else { } else {
const count = (data.errors ? data.errors.filter(error => error.severity === 'error').length : 0 + data.error ? 1 : 0) const count = (data.errors ? data.errors.filter(error => error.severity === 'error').length : 0 + data.error ? 1 : 0)
this.events.emit('statusChanged', {key: count, title: 'compilation failed', type: 'error'}) this.emit('statusChanged', {key: count, title: 'compilation failed', type: 'error'})
} }
// Update contract Selection // Update contract Selection
let contractMap = {} let contractMap = {}
@ -395,6 +398,7 @@ class CompileTab extends CompilerApi {
render () { render () {
if (this._view.el) return this._view.el if (this._view.el) return this._view.el
this.onActivationInternal()
this.listenToEvents() this.listenToEvents()
this._view.errorContainer = yo`<div class="${css.errorBlobs} p-2"></div>` this._view.errorContainer = yo`<div class="${css.errorBlobs} p-2"></div>`
this._view.contractSelection = this.contractSelection() this._view.contractSelection = this.contractSelection()

@ -1,15 +1,15 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var css = require('./styles/debugger-tab-styles') var css = require('./styles/debugger-tab-styles')
var DebuggerUI = require('../debugger/debuggerUI') var DebuggerUI = require('./debugger/debuggerUI')
import { BaseApi } from 'remix-plugin' import { ViewPlugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
const profile = { const profile = {
name: 'debugger', name: 'debugger',
displayName: 'Debugger', displayName: 'Debugger',
methods: [], methods: ['debug'],
events: [], events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDk2MHEwIDI2LTE5IDQ1dC00NSAxOWgtMjI0cTAgMTcxLTY3IDI5MGwyMDggMjA5cTE5IDE5IDE5IDQ1dC0xOSA0NXEtMTggMTktNDUgMTl0LTQ1LTE5bC0xOTgtMTk3cS01IDUtMTUgMTN0LTQyIDI4LjUtNjUgMzYuNS04MiAyOS05NyAxM3YtODk2aC0xMjh2ODk2cS01MSAwLTEwMS41LTEzLjV0LTg3LTMzLTY2LTM5LTQzLjUtMzIuNWwtMTUtMTQtMTgzIDIwN3EtMjAgMjEtNDggMjEtMjQgMC00My0xNi0xOS0xOC0yMC41LTQ0LjV0MTUuNS00Ni41bDIwMi0yMjdxLTU4LTExNC01OC0yNzRoLTIyNHEtMjYgMC00NS0xOXQtMTktNDUgMTktNDUgNDUtMTloMjI0di0yOTRsLTE3My0xNzNxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5IDQ1IDE5bDE3MyAxNzNoODQ0bDE3My0xNzNxMTktMTkgNDUtMTl0NDUgMTkgMTkgNDUtMTkgNDVsLTE3MyAxNzN2Mjk0aDIyNHEyNiAwIDQ1IDE5dDE5IDQ1em0tNDgwLTU3NmgtNjQwcTAtMTMzIDkzLjUtMjI2LjV0MjI2LjUtOTMuNSAyMjYuNSA5My41IDkzLjUgMjI2LjV6Ii8+PC9zdmc+', icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDk2MHEwIDI2LTE5IDQ1dC00NSAxOWgtMjI0cTAgMTcxLTY3IDI5MGwyMDggMjA5cTE5IDE5IDE5IDQ1dC0xOSA0NXEtMTggMTktNDUgMTl0LTQ1LTE5bC0xOTgtMTk3cS01IDUtMTUgMTN0LTQyIDI4LjUtNjUgMzYuNS04MiAyOS05NyAxM3YtODk2aC0xMjh2ODk2cS01MSAwLTEwMS41LTEzLjV0LTg3LTMzLTY2LTM5LTQzLjUtMzIuNWwtMTUtMTQtMTgzIDIwN3EtMjAgMjEtNDggMjEtMjQgMC00My0xNi0xOS0xOC0yMC41LTQ0LjV0MTUuNS00Ni41bDIwMi0yMjdxLTU4LTExNC01OC0yNzRoLTIyNHEtMjYgMC00NS0xOXQtMTktNDUgMTktNDUgNDUtMTloMjI0di0yOTRsLTE3My0xNzNxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5IDQ1IDE5bDE3MyAxNzNoODQ0bDE3My0xNzNxMTktMTkgNDUtMTl0NDUgMTkgMTkgNDUtMTkgNDVsLTE3MyAxNzN2Mjk0aDIyNHEyNiAwIDQ1IDE5dDE5IDQ1em0tNDgwLTU3NmgtNjQwcTAtMTMzIDkzLjUtMjI2LjV0MjI2LjUtOTMuNSAyMjYuNSA5My41IDkzLjUgMjI2LjV6Ii8+PC9zdmc+',
description: 'Debug transactions', description: 'Debug transactions',
@ -19,7 +19,7 @@ const profile = {
version: packageJson.version version: packageJson.version
} }
class DebuggerTab extends BaseApi { class DebuggerTab extends ViewPlugin {
constructor () { constructor () {
super(profile) super(profile)
@ -33,11 +33,14 @@ class DebuggerTab extends BaseApi {
<div class="${css.debuggerTabView}" id="debugView"> <div class="${css.debuggerTabView}" id="debugView">
<div id="debugger" class="${css.debugger}"></div> <div id="debugger" class="${css.debugger}"></div>
</div>` </div>`
this.debuggerUI = new DebuggerUI(this.el.querySelector('#debugger')) this.debuggerUI = new DebuggerUI(this.el.querySelector('#debugger'))
return this.el return this.el
} }
debug (hash) {
if (this.debugger) this.debuggerUI.debug(hash)
}
debugger () { debugger () {
return this.debuggerUI return this.debuggerUI
} }

@ -1,16 +1,16 @@
var TxBrowser = require('./debuggerUI/TxBrowser') var TxBrowser = require('./debuggerUI/TxBrowser')
var StepManagerUI = require('./debuggerUI/StepManager') var StepManagerUI = require('./debuggerUI/StepManager')
var VmDebugger = require('./debuggerUI/VmDebugger') var VmDebugger = require('./debuggerUI/VmDebugger')
var toaster = require('../ui/tooltip') var toaster = require('../../ui/tooltip')
var Debugger = require('remix-debug').TransactionDebugger var Debugger = require('remix-debug').TransactionDebugger
var SourceHighlighter = require('../editor/sourceHighlighter') var SourceHighlighter = require('../../editor/sourceHighlighter')
var EventManager = require('../../lib/events') var EventManager = require('../../../lib/events')
var executionContext = require('../../execution-context') var executionContext = require('../../../execution-context')
var globalRegistry = require('../../global/registry') var globalRegistry = require('../../../global/registry')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')

@ -1,5 +1,5 @@
'use strict' 'use strict'
var EventManager = require('../../../lib/events') var EventManager = require('../../../../lib/events')
var yo = require('yo-yo') var yo = require('yo-yo')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')

@ -1,5 +1,5 @@
'use strict' 'use strict'
var EventManager = require('../../../lib/events') var EventManager = require('../../../../lib/events')
var yo = require('yo-yo') var yo = require('yo-yo')
class Slider { class Slider {

@ -1,4 +1,4 @@
var EventManager = require('../../../lib/events') var EventManager = require('../../../../lib/events')
var yo = require('yo-yo') var yo = require('yo-yo')
var ButtonNavigator = require('./ButtonNavigator') var ButtonNavigator = require('./ButtonNavigator')

@ -1,4 +1,4 @@
var EventManager = require('../../../lib/events') var EventManager = require('../../../../lib/events')
var yo = require('yo-yo') var yo = require('yo-yo')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')

@ -2,7 +2,7 @@
var style = require('../styles/basicStyles') var style = require('../styles/basicStyles')
var yo = require('yo-yo') var yo = require('yo-yo')
var DropdownPanel = require('./DropdownPanel') var DropdownPanel = require('./DropdownPanel')
var EventManager = require('../../../../lib/events') var EventManager = require('../../../../../lib/events')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var css = csjs` var css = csjs`

@ -1,8 +1,8 @@
'use strict' 'use strict'
var yo = require('yo-yo') var yo = require('yo-yo')
const copy = require('copy-text-to-clipboard') const copy = require('copy-text-to-clipboard')
var EventManager = require('../../../../lib/events') var EventManager = require('../../../../../lib/events')
var TreeView = require('../../../ui/TreeView') // TODO setup a direct reference to the UI components var TreeView = require('../../../../ui/TreeView') // TODO setup a direct reference to the UI components
var csjs = require('csjs-inject') var csjs = require('csjs-inject')

@ -1,5 +1,5 @@
'use strict' 'use strict'
var EventManager = require('../../../../lib/events') var EventManager = require('../../../../../lib/events')
var DropdownPanel = require('./DropdownPanel') var DropdownPanel = require('./DropdownPanel')
var solidityTypeFormatter = require('./utils/SolidityTypeFormatter') var solidityTypeFormatter = require('./utils/SolidityTypeFormatter')
var yo = require('yo-yo') var yo = require('yo-yo')

@ -1,18 +1,20 @@
const executionContext = require('../../execution-context') const executionContext = require('../../execution-context')
import { NetworkApi } from 'remix-plugin' import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
export const profile = { export const profile = {
name: 'network', name: 'network',
description: 'Manage the network (mainnet, ropsten, goerli...) and the provider (web3, vm, injected)', description: 'Manage the network (mainnet, ropsten, goerli...) and the provider (web3, vm, injected)',
version: packageJson.version methods: [],
version: packageJson.version,
required: true
} }
// Network API has : // Network API has :
// - events: ['providerChanged'] // - events: ['providerChanged']
// - methods: ['getNetworkProvider', 'getEndpoint', 'detectNetwork', 'addNetwork', 'removeNetwork'] // - methods: ['getNetworkProvider', 'getEndpoint', 'detectNetwork', 'addNetwork', 'removeNetwork']
export class NetworkModule extends NetworkApi { export class NetworkModule extends Plugin {
constructor () { constructor () {
super(profile) super(profile)
// TODO: See with remix-lib to make sementic coherent // TODO: See with remix-lib to make sementic coherent

@ -1,3 +1,4 @@
var $ = require('jquery')
var yo = require('yo-yo') var yo = require('yo-yo')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var Card = require('../ui/card') var Card = require('../ui/card')
@ -8,13 +9,14 @@ var SettingsUI = require('./runTab/settings.js')
var DropdownLogic = require('./runTab/model/dropdownlogic.js') var DropdownLogic = require('./runTab/model/dropdownlogic.js')
var ContractDropdownUI = require('./runTab/contractDropdown.js') var ContractDropdownUI = require('./runTab/contractDropdown.js')
var UniversalDAppUI = require('../ui/universal-dapp-ui')
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')
const executionContext = require('../../execution-context') const executionContext = require('../../execution-context')
import { BaseApi } from 'remix-plugin' import { ViewPlugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
const profile = { const profile = {
@ -30,20 +32,46 @@ const profile = {
version: packageJson.version version: packageJson.version
} }
class RunTab extends BaseApi { class RunTab extends ViewPlugin {
constructor (udapp, udappUI, config, fileManager, editor, logCallback, filePanel, pluginManager, compilersArtefacts) { constructor (udapp, config, fileManager, editor, filePanel, compilersArtefacts, networkModule, mainView) {
super(profile) super(profile)
this.event = new EventManager() this.event = new EventManager()
this.config = config this.config = config
this.udapp = udapp this.udapp = udapp
this.udappUI = udappUI
this.fileManager = fileManager this.fileManager = fileManager
this.editor = editor this.editor = editor
this.logCallback = logCallback this.logCallback = (msg) => { mainView.getTerminal().logHtml(msg) }
this.filePanel = filePanel this.filePanel = filePanel
this.pluginManager = pluginManager
this.compilersArtefacts = compilersArtefacts this.compilersArtefacts = compilersArtefacts
this.networkModule = networkModule
}
onActivationInternal () {
this.udappUI = new UniversalDAppUI(this.udapp, this.logCallback)
this.udapp.resetAPI({
getAddress: (cb) => {
cb(null, $('#txorigin').val())
},
getValue: (cb) => {
try {
var number = document.querySelector('#value').value
var select = document.getElementById('unit')
var index = select.selectedIndex
var selectedUnit = select.querySelectorAll('option')[index].dataset.unit
var unit = 'ether' // default
if (['ether', 'finney', 'gwei', 'wei'].indexOf(selectedUnit) >= 0) {
unit = selectedUnit
}
cb(null, executionContext.web3().toWei(number, unit))
} catch (e) {
cb(e)
}
},
getGasLimit: (cb) => {
cb(null, $('#gasLimit').val())
}
})
} }
renderContainer () { renderContainer () {
@ -90,15 +118,15 @@ class RunTab extends BaseApi {
renderSettings (udapp) { renderSettings (udapp) {
var settings = new Settings(udapp) var settings = new Settings(udapp)
this.settingsUI = new SettingsUI(settings) this.settingsUI = new SettingsUI(settings, this.networkModule)
this.settingsUI.event.register('clearInstance', () => { this.settingsUI.event.register('clearInstance', () => {
this.event.trigger('clearInstance', []) this.event.trigger('clearInstance', [])
}) })
} }
renderDropdown (udappUI, fileManager, pluginManager, compilersArtefacts, config, editor, udapp, filePanel, logCallback) { renderDropdown (udappUI, fileManager, compilersArtefacts, config, editor, udapp, filePanel, logCallback) {
var dropdownLogic = new DropdownLogic(fileManager, pluginManager, compilersArtefacts, config, editor, udapp, filePanel) var dropdownLogic = new DropdownLogic(fileManager, compilersArtefacts, config, editor, udapp, filePanel, this)
this.contractDropdownUI = new ContractDropdownUI(dropdownLogic, logCallback) this.contractDropdownUI = new ContractDropdownUI(dropdownLogic, logCallback)
this.contractDropdownUI.event.register('clearInstance', () => { this.contractDropdownUI.event.register('clearInstance', () => {
@ -166,13 +194,14 @@ class RunTab extends BaseApi {
} }
render () { render () {
this.onActivationInternal()
executionContext.init(this.config) executionContext.init(this.config)
executionContext.stopListenOnLastBlock() executionContext.stopListenOnLastBlock()
executionContext.listenOnLastBlock() executionContext.listenOnLastBlock()
this.udapp.resetEnvironment() this.udapp.resetEnvironment()
this.renderInstanceContainer() this.renderInstanceContainer()
this.renderSettings(this.udapp) this.renderSettings(this.udapp)
this.renderDropdown(this.udappUI, this.fileManager, this.pluginManager, this.compilersArtefacts, this.config, this.editor, this.udapp, this.filePanel, this.logCallback) this.renderDropdown(this.udappUI, this.fileManager, this.compilersArtefacts, this.config, this.editor, this.udapp, this.filePanel, this.logCallback)
this.renderRecorder(this.udapp, this.udappUI, this.fileManager, this.config, this.logCallback) this.renderRecorder(this.udapp, this.udappUI, this.fileManager, this.config, this.logCallback)
this.renderRecorderCard() this.renderRecorderCard()
return this.renderContainer() return this.renderContainer()

@ -3,9 +3,9 @@ var css = require('../styles/run-tab-styles')
var modalDialogCustom = require('../../ui/modal-dialog-custom') var modalDialogCustom = require('../../ui/modal-dialog-custom')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var confirmDialog = require('../../execution/confirmDialog') var confirmDialog = require('../../ui/confirmDialog')
var modalDialog = require('../../ui/modaldialog') var modalDialog = require('../../ui/modaldialog')
var MultiParamManager = require('../../../multiParamManager') var MultiParamManager = require('../../ui/multiParamManager')
class ContractDropdownUI { class ContractDropdownUI {
constructor (dropdownLogic, logCallback) { constructor (dropdownLogic, logCallback) {

@ -9,12 +9,12 @@ var CompilerAbstract = require('../../../compiler/compiler-abstract')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
class DropdownLogic { class DropdownLogic {
constructor (fileManager, pluginManager, compilersArtefacts, config, editor, udapp, filePanel) { constructor (fileManager, compilersArtefacts, config, editor, udapp, filePanel, runView) {
this.pluginManager = pluginManager
this.compilersArtefacts = compilersArtefacts this.compilersArtefacts = compilersArtefacts
this.config = config this.config = config
this.editor = editor this.editor = editor
this.udapp = udapp this.udapp = udapp
this.runView = runView
this.filePanel = filePanel this.filePanel = filePanel
this.event = new EventManager() this.event = new EventManager()
@ -28,7 +28,7 @@ class DropdownLogic {
// TODO: can be moved up; the event in contractDropdown will have to refactored a method instead // TODO: can be moved up; the event in contractDropdown will have to refactored a method instead
listenToCompilationEvents () { listenToCompilationEvents () {
this.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { this.runView.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
// TODO check whether the tab is configured // TODO check whether the tab is configured
let compiler = new CompilerAbstract(languageVersion, data, source) let compiler = new CompilerAbstract(languageVersion, data, source)
this.compilersArtefacts[languageVersion] = compiler this.compilersArtefacts[languageVersion] = compiler
@ -264,29 +264,32 @@ class DropdownLogic {
this.udapp.runTx(data, confirmationCb, continueCb, promptCb, finalCb) this.udapp.runTx(data, confirmationCb, continueCb, promptCb, finalCb)
} }
forceSend (selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, cb) { async forceSend (selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, cb) {
var constructor = selectedContract.getConstructorInterface() var constructor = selectedContract.getConstructorInterface()
// TODO: deployMetadataOf can be moved here // TODO: deployMetadataOf can be moved here
this.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { let contractMetadata
if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) try {
if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { contractMetadata = await this.runView.call('compilerMetadata', 'deployMetadataOf', selectedContract.name)
return txFormat.buildData(selectedContract.name, selectedContract.object, this.compilersArtefacts['__last'].getData().contracts, true, constructor, args, (error, data) => { } catch (error) {
if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) return statusCb(`creation of ${selectedContract.name} errored: ` + error)
}
statusCb(`creation of ${selectedContract.name} pending...`) if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) {
this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) return txFormat.buildData(selectedContract.name, selectedContract.object, this.compilersArtefacts['__last'].getData().contracts, true, constructor, args, (error, data) => {
}, statusCb, (data, runTxCallback) => {
// called for libraries deployment
this.runTransaction(data, continueCb, promptCb, modalDialog, confirmDialog, runTxCallback)
})
}
if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`)
txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => {
if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error)
statusCb(`creation of ${selectedContract.name} pending...`) statusCb(`creation of ${selectedContract.name} pending...`)
this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb)
}, statusCb, (data, runTxCallback) => {
// called for libraries deployment
this.runTransaction(data, continueCb, promptCb, modalDialog, confirmDialog, runTxCallback)
}) })
}
if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`)
txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => {
if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error)
statusCb(`creation of ${selectedContract.name} pending...`)
this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb)
}) })
} }

@ -6,7 +6,7 @@ var css = require('../styles/run-tab-styles')
var modalDialogCustom = require('../../ui/modal-dialog-custom') var modalDialogCustom = require('../../ui/modal-dialog-custom')
var modalDialog = require('../../ui/modaldialog') var modalDialog = require('../../ui/modaldialog')
var confirmDialog = require('../../execution/confirmDialog') var confirmDialog = require('../../ui/confirmDialog')
class RecorderUI { class RecorderUI {

@ -11,7 +11,7 @@ const globalRegistry = require('../../../global/registry')
class SettingsUI { class SettingsUI {
constructor (settings) { constructor (settings, networkModule) {
this.settings = settings this.settings = settings
this.event = new EventManager() this.event = new EventManager()
this._components = {} this._components = {}
@ -21,10 +21,12 @@ class SettingsUI {
if (!lookupOnly) this.el.querySelector('#value').value = '0' if (!lookupOnly) this.el.querySelector('#value').value = '0'
this.updateAccountBalances() this.updateAccountBalances()
}) })
this._components = {
registry: globalRegistry,
networkModule: networkModule
}
this._components.registry = globalRegistry this._components.registry = globalRegistry
this._deps = { this._deps = {
networkModule: this._components.registry.get('network').api,
config: this._components.registry.get('config').api config: this._components.registry.get('config').api
} }
@ -307,7 +309,7 @@ class SettingsUI {
this.netUI.innerHTML = 'can\'t detect network ' this.netUI.innerHTML = 'can\'t detect network '
return return
} }
let network = this._deps.networkModule.getNetworkProvider let network = this._components.networkModule.getNetworkProvider
this.netUI.innerHTML = (network() !== 'vm') ? `${name} (${id || '-'}) network` : '' this.netUI.innerHTML = (network() !== 'vm') ? `${name} (${id || '-'}) network` : ''
}) })
this.fillAccountsList() this.fillAccountsList()

@ -4,7 +4,7 @@ var tooltip = require('../ui/tooltip')
var copyToClipboard = require('../ui/copy-to-clipboard') var copyToClipboard = require('../ui/copy-to-clipboard')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var css = require('./styles/settings-tab-styles') var css = require('./styles/settings-tab-styles')
import { BaseApi } from 'remix-plugin' import { ViewPlugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
const profile = { const profile = {
@ -20,7 +20,7 @@ const profile = {
version: packageJson.version version: packageJson.version
} }
module.exports = class SettingsTab extends BaseApi { module.exports = class SettingsTab extends ViewPlugin {
constructor (config, editor, appManager) { constructor (config, editor, appManager) {
super(profile) super(profile)
this.config = config this.config = config

@ -5,11 +5,11 @@ var $ = require('jquery')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var utils = remixLib.util var utils = remixLib.util
var css = require('./styles/staticAnalysisView-styles') var css = require('./styles/staticAnalysisView-styles')
var globlalRegistry = require('../../global/registry') var Renderer = require('../../ui/renderer')
var EventManager = require('../../lib/events') var EventManager = require('../../../lib/events')
function staticAnalysisView (localRegistry) { function staticAnalysisView (localRegistry, analysisModule) {
var self = this var self = this
this.event = new EventManager() this.event = new EventManager()
this.view = null this.view = null
@ -17,16 +17,16 @@ function staticAnalysisView (localRegistry) {
this.modulesView = this.renderModules() this.modulesView = this.renderModules()
this.lastCompilationResult = null this.lastCompilationResult = null
this.lastCompilationSource = null this.lastCompilationSource = null
self._components = {} self._components = {
self._components.registry = localRegistry || globlalRegistry renderer: new Renderer()
}
self._components.registry = localRegistry
// dependencies // dependencies
self._deps = { self._deps = {
pluginManager: self._components.registry.get('pluginmanager').api,
renderer: self._components.registry.get('renderer').api,
offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api
} }
self._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { analysisModule.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
self.lastCompilationResult = null self.lastCompilationResult = null
self.lastCompilationSource = null self.lastCompilationSource = null
$('#staticanalysisresult').empty() $('#staticanalysisresult').empty()
@ -119,7 +119,7 @@ staticAnalysisView.prototype.run = function () {
} }
warningCount++ warningCount++
var msg = yo`<span>${location} ${item.warning} ${item.more ? yo`<span><br><a href="${item.more}" target="blank">more</a></span>` : yo`<span></span>`}</span>` var msg = yo`<span>${location} ${item.warning} ${item.more ? yo`<span><br><a href="${item.more}" target="blank">more</a></span>` : yo`<span></span>`}</span>`
self._deps.renderer.error(msg, warningContainer, {type: 'staticAnalysisWarning alert alert-warning', useSpan: true}) self._components.renderer.error(msg, warningContainer, {type: 'staticAnalysisWarning alert alert-warning', useSpan: true})
}) })
}) })
self.event.trigger('staticAnaysisWarning', [warningCount]) self.event.trigger('staticAnaysisWarning', [warningCount])

@ -3,7 +3,7 @@ var async = require('async')
var tooltip = require('../ui/tooltip') var tooltip = require('../ui/tooltip')
var css = require('./styles/test-tab-styles') var css = require('./styles/test-tab-styles')
var remixTests = require('remix-tests') var remixTests = require('remix-tests')
import { BaseApi } from 'remix-plugin' import { ViewPlugin } from '@remixproject/engine'
const TestTabLogic = require('./testTab/testTab') const TestTabLogic = require('./testTab/testTab')
@ -18,32 +18,29 @@ const profile = {
documentation: 'https://remix-ide.readthedocs.io/en/latest/unittesting.html' documentation: 'https://remix-ide.readthedocs.io/en/latest/unittesting.html'
} }
module.exports = class TestTab extends BaseApi { module.exports = class TestTab extends ViewPlugin {
constructor (fileManager, filePanel, compileTab, appStore) { constructor (fileManager, filePanel, compileTab, appManager) {
super(profile) super(profile)
this.compileTab = compileTab this.compileTab = compileTab
this._view = { el: null } this._view = { el: null }
this.compileTab = compileTab this.compileTab = compileTab
this.fileManager = fileManager this.fileManager = fileManager
this.filePanel = filePanel this.filePanel = filePanel
this.appStore = appStore
this.testTabLogic = new TestTabLogic(fileManager)
this.data = {} this.data = {}
this.appStore.event.on('activate', (name) => { this.appManager = appManager
appManager.event.on('activate', (name) => {
if (name === 'solidity') this.updateRunAction(fileManager.currentFile()) if (name === 'solidity') this.updateRunAction(fileManager.currentFile())
}) })
this.appStore.event.on('deactivate', (name) => { appManager.event.on('deactivate', (name) => {
if (name === 'solidity') this.updateRunAction(fileManager.currentFile()) if (name === 'solidity') this.updateRunAction(fileManager.currentFile())
}) })
} }
activate () { onActivationInternal () {
this.testTabLogic = new TestTabLogic(this.fileManager)
this.listenToEvents() this.listenToEvents()
} }
deactivate () {
}
listenToEvents () { listenToEvents () {
this.filePanel.event.register('newTestFileCreated', file => { this.filePanel.event.register('newTestFileCreated', file => {
var testList = this.view.querySelector("[class^='testList']") var testList = this.view.querySelector("[class^='testList']")
@ -144,8 +141,7 @@ module.exports = class TestTab extends BaseApi {
runTest (testFilePath, callback) { runTest (testFilePath, callback) {
this.loading.hidden = false this.loading.hidden = false
this.fileManager.fileProviderOf(testFilePath).get(testFilePath, (error, content) => { this.fileManager.getFile(testFilePath).then((content) => {
if (error) return
var runningTest = {} var runningTest = {}
runningTest[testFilePath] = { content } runningTest[testFilePath] = { content }
remixTests.runTestSources(runningTest, (result) => { this.testCallback(result) }, (_err, result, cb) => { this.resultsCallback(_err, result, cb) }, (error, result) => { remixTests.runTestSources(runningTest, (result) => { this.testCallback(result) }, (_err, result, cb) => { this.resultsCallback(_err, result, cb) }, (error, result) => {
@ -155,6 +151,8 @@ module.exports = class TestTab extends BaseApi {
}, (url, cb) => { }, (url, cb) => {
return this.compileTab.compileTabLogic.importFileCb(url, cb) return this.compileTab.compileTabLogic.importFileCb(url, cb)
}) })
}).catch((error) => {
if (error) return
}) })
} }
@ -183,7 +181,7 @@ module.exports = class TestTab extends BaseApi {
updateRunAction (currentFile) { updateRunAction (currentFile) {
let el = yo`<button class="${css.runButton} btn btn-primary" onclick="${this.runTests.bind(this)}">Run Tests</button>` let el = yo`<button class="${css.runButton} btn btn-primary" onclick="${this.runTests.bind(this)}">Run Tests</button>`
const isSolidityActive = this.appStore.isActive('solidity') const isSolidityActive = this.appManager.isActive('solidity')
if (!currentFile || !isSolidityActive) { if (!currentFile || !isSolidityActive) {
el.setAttribute('disabled', 'disabled') el.setAttribute('disabled', 'disabled')
if (!currentFile) el.setAttribute('title', 'No file selected') if (!currentFile) el.setAttribute('title', 'No file selected')
@ -209,6 +207,7 @@ module.exports = class TestTab extends BaseApi {
return this.testFilesListElement return this.testFilesListElement
} }
render () { render () {
this.onActivationInternal()
this.testsOutput = yo`<div class="${css.container} m-3 border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>` this.testsOutput = yo`<div class="${css.container} m-3 border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
this.testsSummary = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>` this.testsSummary = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
this.loading = yo`<span class='text-info ml-1'>Running tests...</span>` this.loading = yo`<span class='text-info ml-1'>Running tests...</span>`

@ -1,4 +1,4 @@
import { BaseApi } from 'remix-plugin' import { Plugin } from '@remixproject/engine'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
@ -21,10 +21,11 @@ const profile = {
name: 'theme', name: 'theme',
events: ['themeChanged'], events: ['themeChanged'],
methods: ['switchTheme', 'getThemes', 'currentTheme'], methods: ['switchTheme', 'getThemes', 'currentTheme'],
version: packageJson.version version: packageJson.version,
required: true
} }
export class ThemeModule extends BaseApi { export class ThemeModule extends Plugin {
constructor (registry) { constructor (registry) {
super(profile) super(profile)

@ -1,7 +1,7 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var Commands = require('../constants/commands') var Commands = require('../../lib/commands')
// -------------- styling ---------------------- // -------------- styling ----------------------
var css = require('./styles/auto-complete-popup-styles') var css = require('./styles/auto-complete-popup-styles')
@ -189,8 +189,8 @@ class AutoCompletePopup {
extendAutocompletion () { extendAutocompletion () {
// TODO: this is not using the appManager interface. Terminal should be put as module // TODO: this is not using the appManager interface. Terminal should be put as module
this.opts.appStore.event.on('activate', (id) => { this.opts.appManager.event.on('activate', (id) => {
const profile = this.opts.appStore.getOne(id).profile const profile = this.opts.appManager.getOne(id).profile
if (!profile.methods) return if (!profile.methods) return
profile.methods.forEach((method) => { profile.methods.forEach((method) => {
const key = `remix.call({name: '${id}', key:'${method}', payload: []}).then((result) => { console.log(result) }).catch((error) => { console.log(error) })` const key = `remix.call({name: '${id}', key:'${method}', payload: []}).then((result) => { console.log(result) }).catch((error) => { console.log(error) })`

@ -1,6 +1,6 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
const copyToClipboard = require('../ui/copy-to-clipboard') const copyToClipboard = require('./copy-to-clipboard')
var css = csjs` var css = csjs`
.txInfoBox { .txInfoBox {

File diff suppressed because one or more lines are too long

@ -1,5 +1,3 @@
let globalRegistry = require('../../../global/registry')
export class Workspace { export class Workspace {
constructor (title, description, isMain, activate, deactivate) { constructor (title, description, isMain, activate, deactivate) {
this.title = title this.title = title
@ -21,7 +19,6 @@ export const defaultWorkspaces = (appManager) => {
appManager.ensureActivated('run') appManager.ensureActivated('run')
appManager.ensureActivated('solidityStaticAnalysis') appManager.ensureActivated('solidityStaticAnalysis')
appManager.ensureActivated('solidityUnitTesting') appManager.ensureActivated('solidityUnitTesting')
globalRegistry.get('verticalicon').api.select('solidity')
}, () => {}), }, () => {}),
new Workspace( new Workspace(
'Vyper', 'Vyper',
@ -30,7 +27,6 @@ export const defaultWorkspaces = (appManager) => {
() => { () => {
appManager.ensureActivated('vyper') appManager.ensureActivated('vyper')
appManager.ensureActivated('run') appManager.ensureActivated('run')
globalRegistry.get('verticalicon').api.select('vyper')
}, () => {}), }, () => {}),
new Workspace('Debugger', 'Debug transactions with remix', false, () => { new Workspace('Debugger', 'Debug transactions with remix', false, () => {
appManager.ensureActivated('debugger') appManager.ensureActivated('debugger')

@ -1,8 +1,8 @@
'use strict' 'use strict'
var yo = require('yo-yo') var yo = require('yo-yo')
var css = require('./universal-dapp-styles') var css = require('../../universal-dapp-styles')
var copyToClipboard = require('./app/ui/copy-to-clipboard') var copyToClipboard = require('./copy-to-clipboard')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var txFormat = remixLib.execution.txFormat var txFormat = remixLib.execution.txFormat

@ -1,8 +1,8 @@
/* global localStorage */ /* global localStorage */
const yo = require('yo-yo') const yo = require('yo-yo')
const csjs = require('csjs-inject') const csjs = require('csjs-inject')
const addTooltip = require('./app/ui/tooltip') const addTooltip = require('./tooltip')
const modalDialog = require('./app/ui/modaldialog') const modalDialog = require('./modaldialog')
const css = csjs` const css = csjs`
.permission h4 { .permission h4 {

@ -1,6 +1,6 @@
'use strict' 'use strict'
var yo = require('yo-yo') var yo = require('yo-yo')
var copyToClipboard = require('../ui/copy-to-clipboard') var copyToClipboard = require('./copy-to-clipboard')
// -------------- styling ---------------------- // -------------- styling ----------------------
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
@ -9,7 +9,7 @@ var remixLib = require('remix-lib')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var helper = require('../../lib/helper') var helper = require('../../lib/helper')
var executionContext = require('../../execution-context') var executionContext = require('../../execution-context')
var modalDialog = require('../ui/modal-dialog-custom') var modalDialog = require('./modal-dialog-custom')
var typeConversion = remixLib.execution.typeConversion var typeConversion = remixLib.execution.typeConversion
var globlalRegistry = require('../../global/registry') var globlalRegistry = require('../../global/registry')
@ -117,7 +117,7 @@ var css = csjs`
* *
*/ */
class TxLogger { class TxLogger {
constructor (localRegistry) { constructor (eventsDecoder, txListener, terminal) {
this.event = new EventManager() this.event = new EventManager()
this.seen = {} this.seen = {}
function filterTx (value, query) { function filterTx (value, query) {
@ -126,18 +126,15 @@ class TxLogger {
} }
return false return false
} }
this.eventsDecoder = eventsDecoder
this._components = {} this.txListener = txListener
this._components.registry = localRegistry || globlalRegistry this.terminal = terminal
// dependencies // dependencies
this._deps = { this._deps = {
mainView: this._components.registry.get('mainview').api, compilersArtefacts: globlalRegistry.get('compilersartefacts').api
txListener: this._components.registry.get('txlistener').api,
eventsDecoder: this._components.registry.get('eventsdecoder').api,
compilersArtefacts: this._components.registry.get('compilersartefacts').api
} }
this.logKnownTX = this._deps.mainView.registerCommand('knownTransaction', (args, cmds, append) => { this.logKnownTX = this.terminal.registerCommand('knownTransaction', (args, cmds, append) => {
var data = args[0] var data = args[0]
var el var el
if (data.tx.isCall) { if (data.tx.isCall) {
@ -149,35 +146,35 @@ class TxLogger {
append(el) append(el)
}, { activate: true, filterFn: filterTx }) }, { activate: true, filterFn: filterTx })
this.logUnknownTX = this._deps.mainView.registerCommand('unknownTransaction', (args, cmds, append) => { this.logUnknownTX = this.terminal.registerCommand('unknownTransaction', (args, cmds, append) => {
// triggered for transaction AND call // triggered for transaction AND call
var data = args[0] var data = args[0]
var el = renderUnknownTransaction(this, data) var el = renderUnknownTransaction(this, data)
append(el) append(el)
}, { activate: false, filterFn: filterTx }) }, { activate: false, filterFn: filterTx })
this.logEmptyBlock = this._deps.mainView.registerCommand('emptyBlock', (args, cmds, append) => { this.logEmptyBlock = this.terminal.registerCommand('emptyBlock', (args, cmds, append) => {
var data = args[0] var data = args[0]
var el = renderEmptyBlock(this, data) var el = renderEmptyBlock(this, data)
append(el) append(el)
}, { activate: true }) }, { activate: true })
this._deps.txListener.event.register('newBlock', (block) => { this.txListener.event.register('newBlock', (block) => {
if (!block.transactions || block.transactions && !block.transactions.length) { if (!block.transactions || block.transactions && !block.transactions.length) {
this.logEmptyBlock({ block: block }) this.logEmptyBlock({ block: block })
} }
}) })
this._deps.txListener.event.register('newTransaction', (tx, receipt) => { this.txListener.event.register('newTransaction', (tx, receipt) => {
log(this, tx, receipt) log(this, tx, receipt)
}) })
this._deps.txListener.event.register('newCall', (tx) => { this.txListener.event.register('newCall', (tx) => {
log(this, tx, null) log(this, tx, null)
}) })
this._deps.mainView.updateTerminalFilter({ type: 'select', value: 'unknownTransaction' }) this.terminal.updateJournal({ type: 'select', value: 'unknownTransaction' })
this._deps.mainView.updateTerminalFilter({ type: 'select', value: 'knownTransaction' }) this.terminal.updateJournal({ type: 'select', value: 'knownTransaction' })
} }
} }
@ -191,13 +188,13 @@ function debug (e, data, self) {
} }
function log (self, tx, receipt) { function log (self, tx, receipt) {
var resolvedTransaction = self._deps.txListener.resolvedTransaction(tx.hash) var resolvedTransaction = self.txListener.resolvedTransaction(tx.hash)
if (resolvedTransaction) { if (resolvedTransaction) {
var compiledContracts = null var compiledContracts = null
if (self._deps.compilersArtefacts['__last']) { if (self._deps.compilersArtefacts['__last']) {
compiledContracts = self._deps.compilersArtefacts['__last'].getContracts() compiledContracts = self._deps.compilersArtefacts['__last'].getContracts()
} }
self._deps.eventsDecoder.parseLogs(tx, resolvedTransaction.contractName, compiledContracts, (error, logs) => { self.eventsDecoder.parseLogs(tx, resolvedTransaction.contractName, compiledContracts, (error, logs) => {
if (!error) { if (!error) {
self.logKnownTX({ tx: tx, receipt: receipt, resolvedData: resolvedTransaction, logs: logs }) self.logKnownTX({ tx: tx, receipt: receipt, resolvedData: resolvedTransaction, logs: logs })
} }

@ -5,30 +5,26 @@ var $ = require('jquery')
var yo = require('yo-yo') var yo = require('yo-yo')
var ethJSUtil = require('ethereumjs-util') var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN var BN = ethJSUtil.BN
var helper = require('./lib/helper') var helper = require('../../lib/helper')
var copyToClipboard = require('./app/ui/copy-to-clipboard') var copyToClipboard = require('./copy-to-clipboard')
var css = require('./universal-dapp-styles') var css = require('../../universal-dapp-styles')
var MultiParamManager = require('./multiParamManager') var MultiParamManager = require('./multiParamManager')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var typeConversion = remixLib.execution.typeConversion var typeConversion = remixLib.execution.typeConversion
var txExecution = remixLib.execution.txExecution var txExecution = remixLib.execution.txExecution
var txFormat = remixLib.execution.txFormat var txFormat = remixLib.execution.txFormat
var executionContext = require('./execution-context') var executionContext = require('../../execution-context')
var confirmDialog = require('./app/execution/confirmDialog') var confirmDialog = require('./confirmDialog')
var modalCustom = require('./app/ui/modal-dialog-custom') var modalCustom = require('./modal-dialog-custom')
var modalDialog = require('./app/ui/modaldialog') var modalDialog = require('./modaldialog')
var TreeView = require('./app/ui/TreeView') var TreeView = require('./TreeView')
function UniversalDAppUI (udapp, registry) { function UniversalDAppUI (udapp, logCallback) {
this.udapp = udapp this.udapp = udapp
this.registry = registry this.logCallback = logCallback
this.compilerData = {contractsDetails: {}} this.compilerData = {contractsDetails: {}}
this._deps = {
compilersartefacts: registry.get('compilersartefacts').api
}
} }
function decodeResponseToTreeView (response, fnabi) { function decodeResponseToTreeView (response, fnabi) {
@ -251,9 +247,9 @@ UniversalDAppUI.prototype.getCallButton = function (args) {
txFormat.buildData(args.contractName, args.contractAbi, {}, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { txFormat.buildData(args.contractName, args.contractAbi, {}, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => {
if (!error) { if (!error) {
if (!args.funABI.constant) { if (!args.funABI.constant) {
self.registry.get('logCallback').api(`${logMsg} pending ... `) self.logCallback(`${logMsg} pending ... `)
} else { } else {
self.registry.get('logCallback').api(`${logMsg}`) self.logCallback(`${logMsg}`)
} }
if (args.funABI.type === 'fallback') data.dataHex = value if (args.funABI.type === 'fallback') data.dataHex = value
self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, continueCb, promptCb, (error, txResult) => { self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, continueCb, promptCb, (error, txResult) => {
@ -262,7 +258,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) {
if (isVM) { if (isVM) {
var vmError = txExecution.checkVMError(txResult) var vmError = txExecution.checkVMError(txResult)
if (vmError.error) { if (vmError.error) {
self.registry.get('logCallback').api(`${logMsg} errored: ${vmError.message} `) self.logCallback(`${logMsg} errored: ${vmError.message} `)
return return
} }
} }
@ -271,14 +267,14 @@ UniversalDAppUI.prototype.getCallButton = function (args) {
outputCb(decoded) outputCb(decoded)
} }
} else { } else {
self.registry.get('logCallback').api(`${logMsg} errored: ${error} `) self.logCallback(`${logMsg} errored: ${error} `)
} }
}) })
} else { } else {
self.registry.get('logCallback').api(`${logMsg} errored: ${error} `) self.logCallback(`${logMsg} errored: ${error} `)
} }
}, (msg) => { }, (msg) => {
self.registry.get('logCallback').api(msg) self.logCallback(msg)
}, (data, runTxCallback) => { }, (data, runTxCallback) => {
// called for libraries deployment // called for libraries deployment
self.udapp.runTx(data, confirmationCb, runTxCallback) self.udapp.runTx(data, confirmationCb, runTxCallback)

@ -1,26 +1,34 @@
export default { export class FramingService {
start: (appStore, sidePanel, verticalIcon, mainPanel, resizeFeature) => {
sidePanel.events.on('toggle', () => { constructor (sidePanel, verticalIcon, mainView, resizeFeature) {
resizeFeature.panel1.clientWidth !== 0 ? resizeFeature.minimize() : resizeFeature.maximise() this.sidePanel = sidePanel
this.verticalIcon = verticalIcon
this.mainPanel = mainView.getAppPanel()
this.mainView = mainView
this.resizeFeature = resizeFeature
}
start () {
this.sidePanel.events.on('toggle', () => {
this.resizeFeature.panel1.clientWidth !== 0 ? this.resizeFeature.minimize() : this.resizeFeature.maximise()
}) })
sidePanel.events.on('showing', () => { this.sidePanel.events.on('showing', () => {
resizeFeature.panel1.clientWidth === 0 ? resizeFeature.maximise() : '' this.resizeFeature.panel1.clientWidth === 0 ? this.resizeFeature.maximise() : ''
}) })
mainPanel.events.on('toggle', () => { this.mainPanel.events.on('toggle', () => {
resizeFeature.maximise() this.resizeFeature.maximise()
}) })
verticalIcon.select('fileExplorers') this.verticalIcon.select('fileExplorers')
verticalIcon.showHome()
document.addEventListener('keypress', (e) => { document.addEventListener('keypress', (e) => {
if (e.shiftKey && e.ctrlKey) { if (e.shiftKey && e.ctrlKey) {
if (e.code === 'KeyF') { // Ctrl+Shift+F if (e.code === 'KeyF') { // Ctrl+Shift+F
verticalIcon.select('fileExplorers') this.verticalIcon.select('fileExplorers')
} else if (e.code === 'KeyA') { // Ctrl+Shift+A } else if (e.code === 'KeyA') { // Ctrl+Shift+A
verticalIcon.select('pluginManager') this.verticalIcon.select('pluginManager')
} else if (e.code === 'KeyS') { // Ctrl+Shift+S } else if (e.code === 'KeyS') { // Ctrl+Shift+S
verticalIcon.select('settings') this.verticalIcon.select('settings')
} }
e.preventDefault() e.preventDefault()
} }

@ -11,7 +11,8 @@ var globalRegistry = require('../global/registry')
var SourceHighlighter = require('../app/editor/sourceHighlighter') var SourceHighlighter = require('../app/editor/sourceHighlighter')
var RemixDebug = require('remix-debug').EthDebugger var RemixDebug = require('remix-debug').EthDebugger
var TreeView = require('../app/ui/TreeView') // TODO setup a direct reference to the UI components var TreeView = require('../app/ui/TreeView') // TODO setup a direct reference to the UI components
var solidityTypeFormatter = require('../app/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter') var solidityTypeFormatter = require('../app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter')
var GistHandler = require('./gist-handler')
class CmdInterpreterAPI { class CmdInterpreterAPI {
constructor (terminal, localRegistry) { constructor (terminal, localRegistry) {
@ -22,8 +23,8 @@ class CmdInterpreterAPI {
self._components.terminal = terminal self._components.terminal = terminal
self._components.sourceHighlighter = new SourceHighlighter() self._components.sourceHighlighter = new SourceHighlighter()
self._components.fileImport = new CompilerImport() self._components.fileImport = new CompilerImport()
self._components.gistHandler = new GistHandler()
self._deps = { self._deps = {
app: self._components.registry.get('app').api,
fileManager: self._components.registry.get('filemanager').api, fileManager: self._components.registry.get('filemanager').api,
editor: self._components.registry.get('editor').api, editor: self._components.registry.get('editor').api,
compilersArtefacts: self._components.registry.get('compilersartefacts').api, compilersArtefacts: self._components.registry.get('compilersartefacts').api,
@ -138,7 +139,7 @@ class CmdInterpreterAPI {
} }
loadgist (id, cb) { loadgist (id, cb) {
const self = this const self = this
self._deps.app.loadFromGist({gist: id}) self._components.gistHandler.loadFromGist({gist: id}, this._deps.fileManager)
if (cb) cb() if (cb) cb()
} }
loadurl (url, cb) { loadurl (url, cb) {

@ -1,5 +1,7 @@
'use strict' 'use strict'
var modalDialogCustom = require('../app/ui/modal-dialog-custom') var modalDialogCustom = require('../app/ui/modal-dialog-custom')
var request = require('request')
// Allowing window to be overriden for testing // Allowing window to be overriden for testing
function GistHandler (_window) { function GistHandler (_window) {
if (_window !== undefined) { if (_window !== undefined) {
@ -38,6 +40,25 @@ function GistHandler (_window) {
var match = idr.exec(str) var match = idr.exec(str)
return match ? match[0] : null return match ? match[0] : null
} }
this.loadFromGist = (params, fileManager) => {
const gistProvider = fileManager.fileProviderOf('gist')
const self = this
return self.handleLoad(params, function (gistId) {
request.get({
url: `https://api.github.com/gists/${gistId}`,
json: true
}, (error, response, data = {}) => {
if (error || !data.files) {
modalDialogCustom.alert(`Gist load error: ${error || data.message}`)
return
}
fileManager.setBatchFiles(data.files, 'gist', (errorLoadingFile) => {
if (!errorLoadingFile) gistProvider.id = gistId
})
})
})
}
} }
module.exports = GistHandler module.exports = GistHandler

@ -1,29 +1,43 @@
'use strict' 'use strict'
var SourceMappingDecoder = require('remix-lib').SourceMappingDecoder var SourceMappingDecoder = require('remix-lib').SourceMappingDecoder
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../package.json'
function offsetToColumnConverter (appManager) { const profile = {
this.lineBreakPositionsByContent = {} name: 'offsetToLineColumnConverter',
this.sourceMappingDecoder = new SourceMappingDecoder() methods: [],
appManager.data.proxy.event.register('sendCompilationResult', () => { events: [],
this.clear() version: packageJson.version,
}) required: true
} }
offsetToColumnConverter.prototype.offsetToLineColumn = function (rawLocation, file, sources, asts) { export class OffsetToLineColumnConverter extends Plugin {
if (!this.lineBreakPositionsByContent[file]) { constructor () {
for (var filename in asts) { super(profile)
var source = asts[filename] this.lineBreakPositionsByContent = {}
if (source.id === file) { this.sourceMappingDecoder = new SourceMappingDecoder()
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content) }
break
offsetToLineColumn (rawLocation, file, sources, asts) {
if (!this.lineBreakPositionsByContent[file]) {
for (var filename in asts) {
var source = asts[filename]
if (source.id === file) {
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content)
break
}
} }
} }
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
} }
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
}
offsetToColumnConverter.prototype.clear = function () { clear () {
this.lineBreakPositionsByContent = {} this.lineBreakPositionsByContent = {}
} }
module.exports = offsetToColumnConverter activate () {
this.on('solidity', 'compilationFinished', () => {
this.clear()
})
}
}

@ -1,289 +0,0 @@
/* global localStorage */
import { EventEmitter } from 'events'
export class Store {
/**
* Instanciate the store from `localStorage` first
* @template T
* @param {string} name The name of the store
* @param {T} initialState The initial state used if state is not available in `localStorage`
*/
static fromLocal (name, initialState) {
const fromLocal = localStorage.getItem(name)
const intial = fromLocal ? JSON.parse(fromLocal) : initialState
return new Store(name, intial)
}
/**
* Create a Store that hold the state of the component
* @template T
* @param {string} name The name of the store
* @param {T} initialState The initial state of the store
*/
constructor (name, initialState) {
this.event = new EventEmitter()
this.name = name
this.state = initialState || {}
}
/** Listen on event from the store */
get on () {
return this.event.on
}
/** Liste once on event from the store */
get once () {
return this.event.once
}
/**
* Update one field of the state
* @param {Partial<T>} state The part of the state updated
*/
update (state) {
this.state = { ...this.state, ...state }
this.dispatch()
}
/**
* Get one field of the state
* @template Key key of `this.state`
* @param {Key} key A key of the state
*/
get (key) {
return this.state[key]
}
/** Reset the state its initial value */
reset () {
this.state = this.initialState
}
/** Dispatch an event with the new state */
dispatch () {
this.event.emit('newState', this.state)
}
}
/**
* An entity inside a collection
* @typedef {Object} EntityState
* @property {string[]} ids The list of IDs of the entity in the state
* @property {string[]} actives The list of IDs of the activated entities
* @property {Object} entities A map of ids and entities
*/
export class EntityStore extends Store {
/**
* Instanciate the store from `localStorage` first
* @param {string} name The name of the store
* @param {(string|number)} keyId The name of the key used as a unique ID for the entity
* @param {EntityState} initialState The initial state used if state is not available in `localStorage`
*/
static fromLocal (name, keyId, initialState = {}) {
const fromLocal = localStorage.getItem(name)
const intial = fromLocal ? JSON.parse(fromLocal) : initialState
return new EntityStore(name, keyId, intial)
}
/**
* Create a entity Store that hold a map entity of the same model
* @param {string} name The name of the store
* @param {(string|number)} keyId The name of the key used as a unique ID for the entity
* @param {EntityState} initialState The initial state used if state is not available in `localStorage`
*/
constructor (name, keyId, initialState = { ids: [], actives: [], entities: {} }) {
super(name, initialState)
this.keyId = keyId || 'id'
}
/**
* The entities as a Map
* @returns {Object}
*/
get entities () {
return this.state.entities
}
/**
* List of all the ids
* @returns {(string|number)[]}
*/
get ids () {
return this.state.ids
}
/**
* List of all active ID
* @returns {Object[]}
*/
get actives () {
return this.state.actives
}
/**
* Return the length of the entity collection
* @returns {number}
*/
get length () {
return this.state.ids.length
}
/**
* Add a new entity to the state
* @param {Object} entity
*/
add (entity) {
const id = entity[this.keyId]
this.state.entities[id] = entity
this.state.ids.push(id)
this.event.emit('add', entity)
}
/**
* Add entities to the state
* @param {Array} entities
*/
addEntities (entities) {
entities.forEach((entity) => {
if (!entity[this.keyId]) throw new Error(`Key ${this.keyId} doesn't exist in ${entity}`)
this.add(entity[this.keyId], entity)
})
this.event.emit('add', entities)
}
/**
* Remove an entity from the state
* @param {(string|number)} id The id of the entity to remove
*/
remove (id) {
if (!this.state.entities[id]) throw new Error(`No entity with key ${id} found in store ${this.name}`)
delete this.state.entities[id]
this.state.ids.splice(this.state.ids.indexOf(id), 1)
this.state.actives.splice(this.state.ids.indexOf(id), 1)
this.event.emit('remove', id)
}
/** Remove all entity from the state and reset actives and ids to empty */
clear () {
this.state = {
ids: [],
actives: []
}
this.event.emit('clear')
}
/**
* Update one entity of the state
* @param {(string|number)} id The id of the entity to update
* @param {Object} update The fields to update in the entity
*/
updateOne (id, update) {
if (!this.state.entities[id]) throw new Error(`No entity with key ${id} found in store ${this.name}`)
this.state.entities[id] = {
...this.state.entities[id],
...update
}
this.event.emit('update', this.state.entities[id])
}
/**
* Activate one or several entity from the state
* @param {((string|number))} ids An id or a list of id to activate
*/
activate (id) {
if (this.actives.includes(id)) return
this.state.actives.push(id)
this.event.emit('activate', id)
}
/**
* Deactivate one or several entity from the state
* @param {(string|number))} ids An id or a list of id to deactivate
*/
deactivate (id) {
if (!this.actives.includes(id)) return
this.state.actives.splice(this.state.actives.indexOf(id), 1)
this.event.emit('deactivate', id)
}
// /////////
// QUERY //
// /////////
/**
* Get one entity
* @param {(string|number)} id The id of the entity to get
* @returns {Object}
*/
getOne (id) {
return this.state.entities[id]
}
/**
* Get many entities as an array
* @param {(string|number)[]} ids An array of id of entity to get
* @returns {Object[]}
*/
getMany (ids) {
return ids.map(id => this.state.entities[id])
}
/**
* Get all the entities as an array
* @returns {Object[]}
*/
getAll () {
return this.state.ids.map(id => this.state.entities[id])
}
/**
* Get all active entities
* @returns {Object[]}
*/
getActives () {
return this.state.actives.map(id => this.state.entities[id])
}
/**
* Is the entity active
* @param {(string|number)} id The id of the entity to check
* @returns {boolean}
*/
isActive (id) {
return this.state.actives.includes(id)
}
/**
* Is this id inside the store
* @param {(string|number)} id The id of the entity to check
* @returns {boolean}
*/
hasEntity (id) {
return this.state.ids.includes(id)
}
/**
* Is the state empty
* @param {(string|number)} id The id of the entity to check
* @returns {boolean}
*/
isEmpty () {
return this.state.ids.length === 0
}
}
/**
* Store the state of the stores into LocalStorage
* @param {Store[]} stores The list of stores to store into `localStorage`
*/
export function localState (stores) {
stores.forEach(store => {
const name = store.name
store.on('newState', (state) => {
localStorage.setItem(name, JSON.stringify(state))
})
})
}

@ -1,5 +1,5 @@
'use strict' 'use strict'
var executionContext = require('./execution-context') var executionContext = require('../execution-context')
module.exports = class TransactionReceiptResolver { module.exports = class TransactionReceiptResolver {
constructor () { constructor () {

@ -0,0 +1,22 @@
module.exports = (fileManager) => {
// The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event.
let filesToLoad = null
let loadFilesCallback = function (files) { filesToLoad = files } // will be replaced later
window.addEventListener('message', function (ev) {
if (typeof ev.data === typeof [] && ev.data[0] === 'loadFiles') {
loadFilesCallback(ev.data[1])
}
}, false)
// Replace early callback with instant response
loadFilesCallback = function (files) {
fileManager.setBatchFiles(files)
}
// Run if we did receive an event from remote instance while starting up
if (filesToLoad !== null) {
fileManager.setBatchFiles(filesToLoad)
}
}

@ -0,0 +1,48 @@
var UniversalDApp = require('./universal-dapp.js')
var registry = require('./global/registry')
var remixLib = require('remix-lib')
var yo = require('yo-yo')
var executionContext = remixLib.execution.executionContext
var Txlistener = remixLib.execution.txListener
var EventsDecoder = remixLib.execution.EventsDecoder
var TransactionReceiptResolver = require('./lib/transactionReceiptResolver')
module.exports = (compilersArtefacts, logHtmlCallback) => {
// ----------------- UniversalDApp -----------------
const udapp = new UniversalDApp(registry)
// TODO: to remove when possible
udapp.event.register('transactionBroadcasted', (txhash, networkName) => {
var txLink = executionContext.txDetailsLink(networkName, txhash)
if (txLink && logHtmlCallback) logHtmlCallback(yo`<a href="${txLink}" target="_blank">${txLink}</a>`)
})
// ----------------- Tx listener -----------------
const transactionReceiptResolver = new TransactionReceiptResolver()
const txlistener = new Txlistener({
api: {
contracts: function () {
if (compilersArtefacts['__last']) return compilersArtefacts['__last'].getContracts()
return null
},
resolveReceipt: function (tx, cb) {
transactionReceiptResolver.resolve(tx, cb)
}
},
event: {
udapp: udapp.event
}})
registry.put({api: txlistener, name: 'txlistener'})
udapp.startListening(txlistener)
const eventsDecoder = new EventsDecoder({
api: {
resolveReceipt: function (tx, cb) {
transactionReceiptResolver.resolve(tx, cb)
}
}
})
txlistener.startListening()
return {udapp, txlistener, eventsDecoder}
}

@ -1,11 +0,0 @@
module.exports = stacktrace
/*
Not used yet
*/
function stacktrace () {
var _ = Error.prepareStackTrace
Error.prepareStackTrace = (_, stack) => stack
var callsites = new Error().stack
Error.prepareStackTrace = _
return callsites.slice(2).map(x => { return x.getFunctionName() }).reverse().join('.')
}

@ -1,64 +1,58 @@
/* global localStorage */ /* global localStorage */
import { AppManagerApi, Plugin } from 'remix-plugin' import { PluginEngine, IframePlugin } from '@remixproject/engine'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import PluginManagerProxy from './app/components/plugin-manager-proxy' import { PermissionHandler } from './app/ui/persmission-handler'
import { PermissionHandler } from './persmission-handler'
export class RemixAppManager extends AppManagerApi { export class RemixAppManager extends PluginEngine {
constructor (store) { constructor (plugins) {
super(null) super(plugins)
this.permissionHandler = new PermissionHandler() this.permissionHandler = new PermissionHandler()
this.store = store
this.hiddenServices = {}
this.event = new EventEmitter() this.event = new EventEmitter()
this.data = { this.registered = {}
proxy: new PluginManagerProxy()
}
} }
ensureActivated (apiName) { onActivated (plugin) {
if (!this.store.isActive(apiName)) this.activateOne(apiName) localStorage.setItem('workspace', JSON.stringify(this.actives))
this.event.emit('ensureActivated', apiName) this.event.emit('activate', plugin.name)
} }
ensureDeactivated (apiName) { getAll () {
if (this.store.isActive(apiName)) this.deactivateOne(apiName) return Object.keys(this.registered).map((p) => {
this.event.emit('ensureDeactivated', apiName) return this.registered[p]
})
} }
proxy () { getOne (name) {
// that's temporary. should be removed when we can have proper notification registration return this.registered[name]
return this.data.proxy
} }
setActive (name, isActive) { getIds () {
const api = this.getEntity(name) return this.registered.map(el => el.name)
// temp
if (api && (name === 'solidity' || name === 'vyper')) {
isActive ? this.data.proxy.register(name, api) : this.data.proxy.unregister(name, api)
}
isActive ? this.store.activate(name) : this.store.deactivate(name)
if (!isActive) {
this.removeHiddenServices(api)
}
localStorage.setItem('workspace', JSON.stringify(this.store.actives))
} }
getEntity (apiName) { onDeactivation (plugin) {
return this.store.getOne(apiName) localStorage.setItem('workspace', JSON.stringify(this.actives))
this.event.emit('deactivate', plugin.name)
} }
addEntity (api) { onRegistration (plugin) {
this.store.add(api) if (!this.registered) this.registered = {}
this.registered[plugin.name] = plugin
this.event.emit('added', plugin.name)
} }
removeHiddenServices (profile) { ensureActivated (apiName) {
let hiddenServices = this.hiddenServices[profile.name] if (!this.isActive(apiName)) this.activateOne(apiName)
if (hiddenServices) document.body.removeChild(hiddenServices) this.event.emit('ensureActivated', apiName)
}
ensureDeactivated (apiName) {
if (this.isActive(apiName)) this.deactivateOne(apiName)
this.event.emit('ensureDeactivated', apiName)
} }
plugins () { registeredPlugins () {
let vyper = { let vyper = {
name: 'vyper', name: 'vyper',
displayName: 'Vyper', displayName: 'Vyper',
@ -143,12 +137,12 @@ export class RemixAppManager extends AppManagerApi {
location: 'sidePanel' location: 'sidePanel'
} }
return [ return [
new Plugin(pipeline), new IframePlugin(pipeline),
new Plugin(vyper), new IframePlugin(vyper),
new Plugin(etherscan), new IframePlugin(etherscan),
new Plugin(ethdoc), new IframePlugin(ethdoc),
new Plugin(mythx), new IframePlugin(mythx),
new Plugin(provable) new IframePlugin(provable)
] ]
} }
} }

@ -7,7 +7,7 @@ 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 { UdappApi } from 'remix-plugin' import { Plugin } from '@remixproject/engine'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import * as packageJson from '../package.json' import * as packageJson from '../package.json'
@ -16,10 +16,11 @@ const profile = {
displayName: 'universal dapp', displayName: 'universal dapp',
description: 'service - run transaction and access account', description: 'service - run transaction and access account',
permission: true, permission: true,
version: packageJson.version version: packageJson.version,
methods: ['createVMAccount', 'newTransaction', 'sendTransaction', 'getAccounts', 'pendingTransactionsCount']
} }
module.exports = class UniversalDApp extends UdappApi { module.exports = class UniversalDApp extends Plugin {
constructor (registry) { constructor (registry) {
super(profile) super(profile)

Loading…
Cancel
Save