Unverified Commit 16f1424f authored by yann300's avatar yann300 Committed by GitHub

Merge branch 'swap_it' into urlresolver

parents 0fc728f5 d4fe4253
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
}, },
"dependencies": { "dependencies": {
"http-server": "0.9.0", "http-server": "0.9.0",
"remix-plugin": "0.0.1-alpha.33", "remix-plugin": "0.0.1-alpha.39",
"remixd": "0.1.8-alpha.6" "remixd": "0.1.8-alpha.6"
}, },
"repository": { "repository": {
...@@ -178,7 +178,7 @@ ...@@ -178,7 +178,7 @@
"remixd": "remixd -s ./contracts --remix-ide http://127.0.0.1:8080", "remixd": "remixd -s ./contracts --remix-ide http://127.0.0.1:8080",
"selenium": "execr --silent selenium-standalone start", "selenium": "execr --silent selenium-standalone start",
"selenium-install": "selenium-standalone install", "selenium-install": "selenium-standalone install",
"serve": "execr --silent http-server .", "serve": "http-server .",
"serve_debugger": "execr --silent http-server src/app/debugger/remix-debugger", "serve_debugger": "execr --silent http-server src/app/debugger/remix-debugger",
"sourcemap": "exorcist --root ../ build/app.js.map > build/app.js", "sourcemap": "exorcist --root ../ build/app.js.map > build/app.js",
"start": "npm-run-all -lpr serve watch onchange remixd", "start": "npm-run-all -lpr serve watch onchange remixd",
......
...@@ -7,7 +7,6 @@ var async = require('async') ...@@ -7,7 +7,6 @@ var async = require('async')
var request = require('request') var request = require('request')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var EventManager = require('./lib/events') var EventManager = require('./lib/events')
var EventEmitter = require('events')
var registry = require('./global/registry') var registry = require('./global/registry')
var UniversalDApp = require('./universal-dapp.js') var UniversalDApp = require('./universal-dapp.js')
var UniversalDAppUI = require('./universal-dapp-ui.js') var UniversalDAppUI = require('./universal-dapp-ui.js')
...@@ -47,7 +46,6 @@ const CompileTab = require('./app/tabs/compile-tab') ...@@ -47,7 +46,6 @@ const CompileTab = require('./app/tabs/compile-tab')
const SettingsTab = require('./app/tabs/settings-tab') const SettingsTab = require('./app/tabs/settings-tab')
const AnalysisTab = require('./app/tabs/analysis-tab') const AnalysisTab = require('./app/tabs/analysis-tab')
const DebuggerTab = require('./app/tabs/debugger-tab') const DebuggerTab = require('./app/tabs/debugger-tab')
// const SupportTab = require('./app/tabs/support-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')
...@@ -55,8 +53,10 @@ const FilePanel = require('./app/panels/file-panel') ...@@ -55,8 +53,10 @@ const FilePanel = require('./app/panels/file-panel')
import PanelsResize from './lib/panels-resize' import PanelsResize from './lib/panels-resize'
import { EntityStore } from './lib/store' import { EntityStore } from './lib/store'
import { RemixAppManager } from './remixAppManager' import { RemixAppManager } from './remixAppManager'
import { generateHomePage, homepageProfile } from './app/ui/landing-page/generate' import { LandingPage } from './app/ui/landing-page/landing-page'
import framingService from './framingService' import framingService from './framingService'
import { ApiFactory } from 'remix-plugin'
import { TxListenerModule } from './app/tabs/txlistener-module'
var css = csjs` var css = csjs`
html { box-sizing: border-box; } html { box-sizing: border-box; }
...@@ -116,8 +116,9 @@ var css = csjs` ...@@ -116,8 +116,9 @@ var css = csjs`
} }
` `
class App { class App extends ApiFactory {
constructor (api = {}, events = {}, opts = {}) { constructor (api = {}, events = {}, opts = {}) {
super()
var self = this var self = this
this.event = new EventManager() this.event = new EventManager()
self._components = {} self._components = {}
...@@ -173,7 +174,7 @@ class App { ...@@ -173,7 +174,7 @@ class App {
run.apply(self) run.apply(self)
} }
profile () { get profile () {
return { return {
name: 'app', name: 'app',
description: 'service - provides information about current context (network).', description: 'service - provides information about current context (network).',
...@@ -373,26 +374,13 @@ Please make a backup of your contracts and start using http://remix.ethereum.org ...@@ -373,26 +374,13 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
/* /*
that proxy is used by appManager to broadcast new transaction event that proxy is used by appManager to broadcast new transaction event
*/ */
const txListenerModuleProxy = { const txListenerModule = new TxListenerModule(txlistener)
event: new EventEmitter(),
profile () {
return {
name: 'txListener',
displayName: 'transaction listener',
events: ['newTransaction'],
description: 'service - notify new transactions'
}
}
}
txlistener.event.register('newTransaction', (tx) => {
txListenerModuleProxy.event.emit('newTransaction', tx)
})
txlistener.startListening() txlistener.startListening()
// TODO: There are still a lot of dep between editorpanel and filemanager // TODO: There are still a lot of dep between editorpanel and filemanager
let appStore = new EntityStore('module', { actives: [], ids: [], entities: {} }) let appStore = new EntityStore('module', 'name')
const appManager = new RemixAppManager(appStore) const appManager = new RemixAppManager(appStore)
registry.put({api: appManager, name: 'appmanager'}) registry.put({api: appManager, name: 'appmanager'})
...@@ -443,7 +431,15 @@ Please make a backup of your contracts and start using http://remix.ethereum.org ...@@ -443,7 +431,15 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
let filePanel = new FilePanel() let filePanel = new FilePanel()
registry.put({api: filePanel, name: 'filepanel'}) registry.put({api: filePanel, name: 'filepanel'})
let compileTab = new CompileTab(registry) let compileTab = new CompileTab(
registry.get('editor').api,
registry.get('config').api,
registry.get('renderer').api,
registry.get('fileproviders/swarm').api,
registry.get('filemanager').api,
registry.get('fileproviders').api,
registry.get('pluginmanager').api
)
let run = new RunTab( let run = new RunTab(
registry.get('udapp').api, registry.get('udapp').api,
registry.get('udappUI').api, registry.get('udappUI').api,
...@@ -455,36 +451,45 @@ Please make a backup of your contracts and start using http://remix.ethereum.org ...@@ -455,36 +451,45 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
registry.get('pluginmanager').api, registry.get('pluginmanager').api,
registry.get('compilersartefacts').api registry.get('compilersartefacts').api
) )
let settings = new SettingsTab(self._components.registry) 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 support = new SupportTab() const landingPage = new LandingPage(appManager, appStore)
let test = new TestTab(self._components.registry, compileTab) let test = new TestTab(
registry.get('filemanager').api,
registry.get('filepanel').api,
compileTab
)
let sourceHighlighters = registry.get('editor').api.sourceHighlighters let sourceHighlighters = registry.get('editor').api.sourceHighlighters
let configProvider = self._components.filesProviders['config'] let configProvider = self._components.filesProviders['config']
appManager.init([ appManager.init([
{ profile: homepageProfile(), api: generateHomePage(appManager, appStore) }, this.api(),
{ profile: this.profile(), api: this }, landingPage.api(),
{ profile: udapp.profile(), api: udapp }, udapp.api(),
{ profile: fileManager.profile(), api: fileManager }, fileManager.api(),
{ profile: sourceHighlighters.profile(), api: sourceHighlighters }, sourceHighlighters.api(),
{ profile: configProvider.profile(), api: configProvider }, configProvider.api(),
{ profile: txListenerModuleProxy.profile(), api: txListenerModuleProxy }, txListenerModule.api(),
{ profile: filePanel.profile(), api: filePanel }, filePanel.api(),
// { profile: support.profile(), api: support }, // { profile: support.profile(), api: support },
{ profile: settings.profile(), api: settings }, settings.api(),
{ profile: pluginManagerComponent.profile(), api: pluginManagerComponent }]) pluginManagerComponent.api()
])
appManager.registerMany([ appManager.registerMany([
{ profile: compileTab.profile(), api: compileTab }, compileTab.api(),
{ profile: run.profile(), api: run }, run.api(),
{ profile: debug.profile(), api: debug }, debug.api(),
{ profile: analysis.profile(), api: analysis }, analysis.api(),
{ profile: test.profile(), api: test }, test.api(),
{ profile: filePanel.remixdHandle.profile(), api: filePanel.remixdHandle } filePanel.remixdHandle.api(),
...appManager.plugins()
]) ])
appManager.registerMany(appManager.plugins())
framingService.start(appStore, swapPanelApi, verticalIconsApi, mainPanelApi, this._components.resizeFeature) framingService.start(appStore, swapPanelApi, verticalIconsApi, mainPanelApi, this._components.resizeFeature)
...@@ -512,7 +517,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org ...@@ -512,7 +517,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
var txLogger = new TxLogger() // eslint-disable-line var txLogger = new TxLogger() // eslint-disable-line
txLogger.event.register('debuggingRequested', (hash) => { txLogger.event.register('debuggingRequested', (hash) => {
if (!appStore.isActive('debugger')) appManager.activateOne('debugger') if (!appStore.isActive('debugger')) appManager.activateOne('debugger')
appStore.getOne('debugger').api.debugger().debug(hash) debug.debugger().debug(hash)
verticalIconsApi.select('debugger') verticalIconsApi.select('debugger')
}) })
......
...@@ -6,7 +6,7 @@ module.exports = class LocalPlugin { ...@@ -6,7 +6,7 @@ module.exports = class LocalPlugin {
/** /**
* Open a modal to create a local plugin * Open a modal to create a local plugin
* @param {{profile: any, api: any}[]} plugins The list of the plugins in the store * @param {PluginApi[]} plugins The list of the plugins in the store
* @returns {Promise<{api: any, profile: any}>} A promise with the new plugin profile * @returns {Promise<{api: any, profile: any}>} A promise with the new plugin profile
*/ */
open (plugins) { open (plugins) {
...@@ -90,7 +90,7 @@ module.exports = class LocalPlugin { ...@@ -90,7 +90,7 @@ module.exports = class LocalPlugin {
/** /**
* The form to create a local plugin * The form to create a local plugin
* @param {Profile[]} plugins Liste of profile of the plugins * @param {ProfileApi[]} plugins Liste of profile of the plugins
*/ */
form (plugins = []) { form (plugins = []) {
const name = this.profile.name || '' const name = this.profile.name || ''
......
...@@ -2,7 +2,7 @@ const yo = require('yo-yo') ...@@ -2,7 +2,7 @@ const yo = require('yo-yo')
const csjs = require('csjs-inject') const csjs = require('csjs-inject')
const EventEmitter = require('events') const EventEmitter = require('events')
const LocalPlugin = require('./local-plugin') const LocalPlugin = require('./local-plugin')
import { Plugin } from 'remix-plugin' import { Plugin, ApiFactory } from 'remix-plugin'
const css = csjs` const css = csjs`
.pluginSearch { .pluginSearch {
...@@ -34,9 +34,10 @@ const css = csjs` ...@@ -34,9 +34,10 @@ const css = csjs`
} }
` `
class PluginManagerComponent { class PluginManagerComponent extends ApiFactory {
constructor () { constructor () {
super()
this.event = new EventEmitter() this.event = new EventEmitter()
this.views = { this.views = {
root: null, root: null,
...@@ -46,7 +47,7 @@ class PluginManagerComponent { ...@@ -46,7 +47,7 @@ class PluginManagerComponent {
this.filter = '' this.filter = ''
} }
profile () { get profile () {
return { return {
displayName: 'plugin manager', displayName: 'plugin manager',
name: 'pluginManager', name: 'pluginManager',
...@@ -66,15 +67,15 @@ class PluginManagerComponent { ...@@ -66,15 +67,15 @@ class PluginManagerComponent {
this.store = store this.store = store
this.store.event.on('activate', (name) => { this.reRender() }) this.store.event.on('activate', (name) => { this.reRender() })
this.store.event.on('deactivate', (name) => { this.reRender() }) this.store.event.on('deactivate', (name) => { this.reRender() })
this.store.event.on('add', (entity) => { this.reRender() }) this.store.event.on('add', (api) => { this.reRender() })
this.store.event.on('remove', (entity) => { this.reRender() }) this.store.event.on('remove', (api) => { this.reRender() })
} }
renderItem (name) { renderItem (name) {
const mod = this.store.getOne(name) const api = this.store.getOne(name)
if (!mod) return if (!api) return
const isActive = this.store.actives.includes(name) const isActive = this.store.actives.includes(name)
const displayName = (mod.profile.displayName) ? mod.profile.displayName : name const displayName = (api.profile.displayName) ? api.profile.displayName : name
const activationButton = isActive const activationButton = isActive
? yo` ? yo`
...@@ -92,7 +93,7 @@ class PluginManagerComponent { ...@@ -92,7 +93,7 @@ class PluginManagerComponent {
<h6 class="${css.displayName}">${displayName}</h6> <h6 class="${css.displayName}">${displayName}</h6>
${activationButton} ${activationButton}
</div> </div>
<p class="${css.description}">${mod.profile.description}</p> <p class="${css.description}">${api.profile.description}</p>
</article> </article>
` `
} }
...@@ -107,9 +108,7 @@ class PluginManagerComponent { ...@@ -107,9 +108,7 @@ class PluginManagerComponent {
try { try {
const profile = await this.localPlugin.open(this.store.getAll()) const profile = await this.localPlugin.open(this.store.getAll())
if (!profile) return if (!profile) return
const resolveLocaton = (iframe) => this.appManager.resolveLocation(profile, iframe) this.appManager.registerOne(new Plugin(profile))
const api = new Plugin(profile, { resolveLocaton })
this.appManager.registerOne({profile, api})
this.appManager.activateOne(profile.name) this.appManager.activateOne(profile.name)
} catch (err) { } catch (err) {
// TODO : Use an alert to handle this error instead of a console.log // TODO : Use an alert to handle this error instead of a console.log
...@@ -119,11 +118,11 @@ class PluginManagerComponent { ...@@ -119,11 +118,11 @@ class PluginManagerComponent {
render () { render () {
// Filtering helpers // Filtering helpers
const isFiltered = ({profile}) => profile.name.toLowerCase().includes(this.filter) const isFiltered = (api) => api.name.toLowerCase().includes(this.filter)
const isNotRequired = ({profile}) => !profile.required const isNotRequired = ({profile}) => !profile.required
const sortByName = (a, b) => { const sortByName = (a, b) => {
const nameA = a.profile.name.toUpperCase() const nameA = a.name.toUpperCase()
const nameB = b.profile.name.toUpperCase() const nameB = b.name.toUpperCase()
return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0 return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0
} }
...@@ -132,10 +131,10 @@ class PluginManagerComponent { ...@@ -132,10 +131,10 @@ class PluginManagerComponent {
.filter(isFiltered) .filter(isFiltered)
.filter(isNotRequired) .filter(isNotRequired)
.sort(sortByName) .sort(sortByName)
.reduce(({actives, inactives}, {profile}) => { .reduce(({actives, inactives}, api) => {
return this.store.actives.includes(profile.name) return this.store.actives.includes(api.name)
? { actives: [...actives, profile.name], inactives } ? { actives: [...actives, api.name], inactives }
: { inactives: [...inactives, profile.name], actives } : { inactives: [...inactives, api.name], actives }
}, { actives: [], inactives: [] }) }, { actives: [], inactives: [] })
const activeTile = actives.length !== 0 const activeTile = actives.length !== 0
......
...@@ -5,7 +5,7 @@ class SwapPanelApi { ...@@ -5,7 +5,7 @@ class SwapPanelApi {
this.event = new EventEmmitter() this.event = new EventEmmitter()
this.component = swapPanelComponent this.component = swapPanelComponent
this.currentContent this.currentContent
verticalIconsComponent.event.on('showContent', (moduleName) => { verticalIconsComponent.events.on('showContent', (moduleName) => {
if (!swapPanelComponent.contents[moduleName]) return if (!swapPanelComponent.contents[moduleName]) return
if (this.currentContent === moduleName) { if (this.currentContent === moduleName) {
this.event.emit('toggle', moduleName) this.event.emit('toggle', moduleName)
......
...@@ -11,15 +11,10 @@ class SwapPanelComponent { ...@@ -11,15 +11,10 @@ class SwapPanelComponent {
// name of the current displayed content // name of the current displayed content
this.currentNode this.currentNode
appManager.event.on('pluginNeedsLocation', (profile, domEl) => {
if ((profile.prefferedLocation === this.name) || (!profile.prefferedLocation && opt.default)) {
this.add(profile.name, domEl)
}
})
this.store.event.on('activate', (name) => { this.store.event.on('activate', (name) => {
const { profile, api } = this.store.getOne(name) const api = this.store.getOne(name)
if (((profile.prefferedLocation === this.name) || (!profile.prefferedLocation && opt.default)) && const profile = api.profile
if (((profile.location === this.name) || (!profile.location && opt.default)) &&
profile.icon && api.render && typeof api.render === 'function') { profile.icon && api.render && typeof api.render === 'function') {
this.add(name, api.render()) this.add(name, api.render())
} }
...@@ -28,8 +23,8 @@ class SwapPanelComponent { ...@@ -28,8 +23,8 @@ class SwapPanelComponent {
this.store.event.on('deactivate', (name) => { this.store.event.on('deactivate', (name) => {
if (this.contents[name]) this.remove(name) if (this.contents[name]) this.remove(name)
}) })
this.store.event.on('add', (entity) => { }) this.store.event.on('add', (api) => { })
this.store.event.on('remove', (entity) => { }) this.store.event.on('remove', (api) => { })
} }
showContent (moduleName) { showContent (moduleName) {
...@@ -40,8 +35,8 @@ class SwapPanelComponent { ...@@ -40,8 +35,8 @@ class SwapPanelComponent {
} }
this.contents[moduleName].style.display = 'block' this.contents[moduleName].style.display = 'block'
this.currentNode = moduleName this.currentNode = moduleName
var item = this.store.getOne(moduleName) var api = this.store.getOne(moduleName)
this.header.innerHTML = item.profile ? item.profile.displayName : ' - ' this.header.innerHTML = api.profile ? api.profile.displayName : ' - '
return return
} }
} }
......
var yo = require('yo-yo') var yo = require('yo-yo')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
const EventEmmitter = require('events') const EventEmitter = require('events')
// Component // Component
class VerticalIconComponent { class VerticalIconComponent {
constructor (name, appStore) { constructor (name, appStore) {
this.store = appStore this.store = appStore
this.event = new EventEmmitter() this.events = new EventEmitter()
this.icons = {} this.icons = {}
this.iconKind = {} this.iconKind = {}
this.name = name this.name = name
...@@ -16,28 +16,34 @@ class VerticalIconComponent { ...@@ -16,28 +16,34 @@ class VerticalIconComponent {
this.store.event.on('activate', (name) => { this.store.event.on('activate', (name) => {
const { profile } = this.store.getOne(name) const { profile } = this.store.getOne(name)
if (!profile.icon) return if (!profile.icon) return
if (profile.prefferedLocation === this.name || !profile.prefferedLocation) { if (profile.location === this.name || !profile.location) {
this.addIcon(profile) this.addIcon(profile)
} }
}) })
this.store.event.on('deactivate', (name) => { this.store.event.on('deactivate', (name) => {
const item = this.store.getOne(name) const api = this.store.getOne(name)
if (item && this.icons[name]) this.removeIcon(item.profile) if (api && this.icons[name]) this.removeIcon(api.profile)
}) })
this.store.event.on('add', (entity) => { }) this.store.event.on('add', (api) => { })
this.store.event.on('remove', (entity) => { }) this.store.event.on('remove', (api) => { })
} }
addIcon (mod) { /**
let kind = mod.kind || 'other' * Add an icon to the map
this.icons[mod.name] = yo`<div class="${css.icon}" onclick=${(e) => { this._iconClick(mod.name) }} title=${mod.name} ><img src="${mod.icon}" alt="${mod.name}" /></div>` * @param {ModuleProfile} profile The profile of the module
*/
addIcon ({kind, name, icon}) {
this.icons[name] = yo`<div class="${css.icon}" onclick="${(e) => { this._iconClick(name) }}" title="${name}" ><img src="${icon}" alt="${name}" /></div>`
this.iconKind[kind].appendChild(this.icons[mod.name]) this.iconKind[kind || 'other'].appendChild(this.icons[name])
} }
removeIcon (mod) { /**
let kind = mod.kind || 'other' * Remove an icon from the map
if (this.icons[mod.name]) this.iconKind[kind].removeChild(this.icons[mod.name]) * @param {ModuleProfile} profile The profile of the module
*/
removeIcon ({kind, name}) {
if (this.icons[name]) this.iconKind[kind || 'other'].removeChild(this.icons[name])
} }
select (name) { select (name) {
...@@ -53,7 +59,7 @@ class VerticalIconComponent { ...@@ -53,7 +59,7 @@ class VerticalIconComponent {
let activate = this.view.querySelector(`[title="${name}"]`) let activate = this.view.querySelector(`[title="${name}"]`)
if (activate) activate.classList.toggle(`${css.active}`) if (activate) activate.classList.toggle(`${css.active}`)
} }
this.event.emit('showContent', name) this.events.emit('showContent', name)
} }
_iconClick (name) { _iconClick (name) {
......
...@@ -12,8 +12,6 @@ var executionContext = require('../../execution-context') ...@@ -12,8 +12,6 @@ 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')
var Web3Providers = remixLib.vm.Web3Providers
var DummyProvider = remixLib.vm.DummyProvider
var init = remixLib.init var init = remixLib.init
...@@ -30,72 +28,12 @@ var css = csjs` ...@@ -30,72 +28,12 @@ var css = csjs`
} }
` `
class ContextManager {
constructor () {
this.executionContext = executionContext
this.web3 = this.executionContext.web3()
this.event = new EventManager()
}
initProviders () {
this.web3Providers = new Web3Providers()
this.addProvider('DUMMYWEB3', new DummyProvider())
this.switchProvider('DUMMYWEB3')
this.addProvider('vm', this.executionContext.vm())
this.addProvider('injected', this.executionContext.internalWeb3())
this.addProvider('web3', this.executionContext.internalWeb3())
this.switchProvider(this.executionContext.getProvider())
}
getWeb3 () {
return this.web3
}
addProvider (type, obj) {
this.web3Providers.addProvider(type, obj)
this.event.trigger('providerAdded', [type])
}
switchProvider (type) {
var self = this
this.web3Providers.get(type, function (error, obj) {
if (error) {
console.log('provider ' + type + ' not defined')
} else {
self.web3 = obj
self.executionContext.detectNetwork((error, network) => {
if (error || !network) {
self.web3 = obj
} else {
var webDebugNode = init.web3DebugNode(network.name)
self.web3 = (!webDebugNode ? obj : webDebugNode)
}
self.event.trigger('providerChanged', [type, self.web3])
})
self.event.trigger('providerChanged', [type, self.web3])
}
})
}
}
class DebuggerUI { class DebuggerUI {
constructor (container) { constructor (container) {
this.registry = globalRegistry this.registry = globalRegistry
this.event = new EventManager() this.event = new EventManager()
this.executionContext = executionContext
this.contextManager = new ContextManager()
this.contextManager.initProviders()
this.contextManager.event.register('providerChanged', () => {
if (this.debugger) this.debugger.updateWeb3(this.contextManager.getWeb3())
})
this.isActive = false this.isActive = false
this.sourceHighlighter = new SourceHighlighter() this.sourceHighlighter = new SourceHighlighter()
...@@ -173,20 +111,30 @@ class DebuggerUI { ...@@ -173,20 +111,30 @@ class DebuggerUI {
if (compilers['__last']) lastCompilationResult = compilers['__last'] if (compilers['__last']) lastCompilationResult = compilers['__last']
// TODO debugging with source highlight is disabled. see line 98 // TODO debugging with source highlight is disabled. see line 98
executionContext.detectNetwork((error, network) => {
let web3
if (error || !network) {
web3 = init.web3DebugNode(executionContext.web3())
} else {
var webDebugNode = init.web3DebugNode(network.name)
web3 = (!webDebugNode ? executionContext.web3() : webDebugNode)
}
init.extendWeb3(web3)
this.debugger = new Debugger({ this.debugger = new Debugger({
web3: this.contextManager.getWeb3(), web3,
offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api, offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api,
compiler: { lastCompilationResult } compiler: { lastCompilationResult }
}) })
this.listenToEvents() this.listenToEvents()
this.debugger.debugger.updateWeb3(this.executionContext.web3())
this.debugger.debug(blockNumber, txNumber, tx, () => { this.debugger.debug(blockNumber, txNumber, tx, () => {
self.stepManager = new StepManagerUI(this.debugger.step_manager) self.stepManager = new StepManagerUI(this.debugger.step_manager)
self.vmDebugger = new VmDebugger(this.debugger.vmDebuggerLogic) self.vmDebugger = new VmDebugger(this.debugger.vmDebuggerLogic)
self.renderDebugger() self.renderDebugger()
}) })
})
} }
debug (txHash) { debug (txHash) {
......
'use strict' 'use strict'
const SourceHighlighter = require('./sourceHighlighter') const SourceHighlighter = require('./sourceHighlighter')
class SourceHighlighters { import { ApiFactory } from 'remix-plugin'
class SourceHighlighters extends ApiFactory {
constructor () { constructor () {
super()
this.highlighters = {} this.highlighters = {}
} }
profile () { get profile () {
return { return {
displayName: 'source highlighters', displayName: 'source highlighters',
name: 'sourceHighlighters', name: 'sourceHighlighters',
...@@ -16,27 +19,21 @@ class SourceHighlighters { ...@@ -16,27 +19,21 @@ class SourceHighlighters {
} }
} }
// TODO what to do with mod? highlight (lineColumnPos, filePath, hexColor) {
async highlight (mod, lineColumnPos, filePath, hexColor) { const { from } = this.currentRequest
return new Promise((resolve, reject) => {
let position
try { try {
position = JSON.parse(lineColumnPos) const position = JSON.parse(lineColumnPos)
if (!this.highlighters[from]) this.highlighters[from] = new SourceHighlighter()
this.highlighters[from].currentSourceLocation(null)
this.highlighters[from].currentSourceLocationFromfileName(position, filePath, hexColor)
} catch (e) { } catch (e) {
throw e throw e
} }
if (!this.highlighters[mod]) this.highlighters[mod] = new SourceHighlighter()
this.highlighters[mod].currentSourceLocation(null)
this.highlighters[mod].currentSourceLocationFromfileName(position, filePath, hexColor)
resolve()
})
} }
async discardHighlight (mod) { discardHighlight () {
return new Promise((resolve, reject) => { const { from } = this.currentRequest
if (this.highlighters[mod]) this.highlighters[mod].currentSourceLocation(null) if (this.highlighters[from]) this.highlighters[from].currentSourceLocation(null)
resolve()
})
} }
} }
......
...@@ -2,21 +2,35 @@ ...@@ -2,21 +2,35 @@
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
function FilesTree (name, storage) { import { ApiFactory } from 'remix-plugin'
var self = this
var event = new EventManager() class FilesTree extends ApiFactory {
this.event = event
constructor (name, storage) {
super()
this.event = new EventManager()
this.storage = storage
this.type = name this.type = name
this.structFile = '.' + name + '.tree' this.structFile = '.' + name + '.tree'
this.tree = {} this.tree = {}
}
get profile () {
// TODO should make them promisable
return {
name: this.type,
methods: ['get', 'set', 'remove'],
description: 'service - read/write file to the `config` explorer without need of additionnal permission.'
}
}
this.exists = function (path, cb) { exists (path, cb) {
cb(null, this._exists(path)) cb(null, this._exists(path))
} }
function updateRefs (path, type) { updateRefs (path, type) {
var split = path.split('/') // this should be unprefixed path var split = path.split('/') // this should be unprefixed path
var crawlpath = self.tree var crawlpath = this.tree
var intermediatePath = '' var intermediatePath = ''
split.forEach((pathPart, index) => { split.forEach((pathPart, index) => {
intermediatePath += pathPart intermediatePath += pathPart
...@@ -30,91 +44,90 @@ function FilesTree (name, storage) { ...@@ -30,91 +44,90 @@ function FilesTree (name, storage) {
delete crawlpath[intermediatePath] delete crawlpath[intermediatePath]
} }
}) })
storage.set(self.structFile, JSON.stringify(self.tree)) this.storage.set(this.structFile, JSON.stringify(this.tree))
} }
this._exists = function (path) { _exists (path) {
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
return storage.exists(unprefixedpath) return this.storage.exists(unprefixedpath)
} }
this.init = function (cb) { init (cb) {
var tree = storage.get(this.structFile) var tree = this.storage.get(this.structFile)
this.tree = tree ? JSON.parse(tree) : {} this.tree = tree ? JSON.parse(tree) : {}
if (cb) cb() if (cb) cb()
} }
this.get = function (path, cb) { get (path, cb) {
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
var content = storage.get(unprefixedpath) var content = this.storage.get(unprefixedpath)
if (cb) { if (cb) {
cb(null, content) cb(null, content)
} }
return content return content
} }
this.set = function (path, content, cb) { set (path, content, cb) {
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
updateRefs(unprefixedpath, 'add') this.updateRefs(unprefixedpath, 'add')
var exists = storage.exists(unprefixedpath) var exists = this.storage.exists(unprefixedpath)
if (!storage.set(unprefixedpath, content)) { if (!this.storage.set(unprefixedpath, content)) {
if (cb) cb('error updating ' + path) if (cb) cb('error updating ' + path)
return false return false
} }
if (!exists) { if (!exists) {
event.trigger('fileAdded', [this.type + '/' + unprefixedpath, false]) this.event.trigger('fileAdded', [this.type + '/' + unprefixedpath, false])
} else { } else {
event.trigger('fileChanged', [this.type + '/' + unprefixedpath]) this.event.trigger('fileChanged', [this.type + '/' + unprefixedpath])
} }
if (cb) cb() if (cb) cb()
return true return true
} }
this.addReadOnly = function (path, content) { addReadOnly (path, content) {
return this.set(path, content) return this.set(path, content)
} }
this.isReadOnly = function (path) { isReadOnly (path) {
return false return false
} }
this.remove = function (path) { remove (path) {
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
updateRefs(unprefixedpath, 'remove') this.updateRefs(unprefixedpath, 'remove')
if (!this._exists(unprefixedpath)) { if (!this._exists(unprefixedpath)) {
return false return false
} }
if (!storage.remove(unprefixedpath)) { if (!this.storage.remove(unprefixedpath)) {
return false return false
} }
event.trigger('fileRemoved', [this.type + '/' + unprefixedpath]) this.event.trigger('fileRemoved', [this.type + '/' + unprefixedpath])
return true return true
} }
this.rename = function (oldPath, newPath, isFolder) { rename (oldPath, newPath, isFolder) {
var unprefixedoldPath = this.removePrefix(oldPath) var unprefixedoldPath = this.removePrefix(oldPath)
var unprefixednewPath = this.removePrefix(newPath) var unprefixednewPath = this.removePrefix(newPath)
updateRefs(unprefixedoldPath, 'remove') this.updateRefs(unprefixedoldPath, 'remove')
updateRefs(unprefixednewPath, 'add') this.updateRefs(unprefixednewPath, 'add')
if (storage.exists(unprefixedoldPath)) { if (this.storage.exists(unprefixedoldPath)) {
if (!storage.rename(unprefixedoldPath, unprefixednewPath)) { if (!this.storage.rename(unprefixedoldPath, unprefixednewPath)) {
return false return false
} }
event.trigger('fileRenamed', [this.type + '/' + unprefixedoldPath, this.type + '/' + unprefixednewPath, isFolder]) this.event.trigger('fileRenamed', [this.type + '/' + unprefixedoldPath, this.type + '/' + unprefixednewPath, isFolder])
return true return true
} }
return false return false
} }
this.resolveDirectory = function (path, callback) { resolveDirectory (path, callback) {
var self = this
if (path[0] === '/') path = path.substring(1) if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [self.type]: { } }) if (!path) return callback(null, { [this.type]: { } })
var tree = {} var tree = {}
path = self.removePrefix(path) path = this.removePrefix(path)
var split = path.split('/') // this should be unprefixed path var split = path.split('/') // this should be unprefixed path
var crawlpath = self.tree var crawlpath = this.tree
split.forEach((pathPart, index) => { split.forEach((pathPart, index) => {
if (crawlpath[pathPart]) crawlpath = crawlpath[pathPart] if (crawlpath[pathPart]) crawlpath = crawlpath[pathPart]
}) })
...@@ -125,20 +138,12 @@ function FilesTree (name, storage) { ...@@ -125,20 +138,12 @@ function FilesTree (name, storage) {
callback(null, tree) callback(null, tree)
} }
this.removePrefix = function (path) { removePrefix (path) {
path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path
if (path[0] === '/') return path.substring(1) if (path[0] === '/') return path.substring(1)
return path return path
} }
this.profile = function () {
// TODO should make them promisable
return {
name: this.type,
methods: ['get', 'set', 'remove'],
description: 'service - read/write file to the `config` explorer without need of additionnal permission.'
}
}
} }
module.exports = FilesTree module.exports = FilesTree
/* global FileReader */
var async = require('async')
var Gists = require('gists')
var modalDialogCustom = require('../ui/modal-dialog-custom')
var tooltip = require('../ui/tooltip')
var QueryParams = require('../../lib/query-params')
var helper = require('../../lib/helper')
var yo = require('yo-yo') var yo = require('yo-yo')
var Treeview = require('../ui/TreeView') var Treeview = require('../ui/TreeView')
var modalDialog = require('../ui/modaldialog') var modalDialog = require('../ui/modaldialog')
var modalDialogCustom = require('../ui/modal-dialog-custom')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var contextMenu = require('../ui/contextMenu') var contextMenu = require('../ui/contextMenu')
var addTooltip = require('../ui/tooltip') var addTooltip = require('../ui/tooltip')
var helper = require('../../lib/helper')
var css = require('./styles/file-explorer-styles') var css = require('./styles/file-explorer-styles')
var globalRegistry = require('../../global/registry') var globalRegistry = require('../../global/registry')
var queryParams = new QueryParams()
let MENU_HANDLE let MENU_HANDLE
function fileExplorer (localRegistry, files) { function fileExplorer (localRegistry, files, menuItems) {
var self = this var self = this
this.events = new EventManager() this.events = new EventManager()
// file provider backend // file provider backend
...@@ -22,6 +25,35 @@ function fileExplorer (localRegistry, files) { ...@@ -22,6 +25,35 @@ function fileExplorer (localRegistry, files) {
this.focusElement = null this.focusElement = null
// path currently focused on // path currently focused on
this.focusPath = null this.focusPath = null
let allItems =
[
{ action: 'createNewFile',
title: 'Create New File in the Browser Storage Explorer',
icon: 'fa fa-plus-circle'
},
{ action: 'publishToGist',
title: 'Publish all [browser] explorer files to a github gist',
icon: 'fa fa-github'
},
{ action: 'copyFiles',
title: 'Copy all files to another instance of Remix IDE',
icon: 'fa fa-files-o'
},
{ action: 'uploadFile',
title: 'Add Local file to the Browser Storage Explorer',
icon: 'fa fa-folder-open'
},
{ action: 'updateGist',
title: 'Update the current [gist] explorer',
icon: 'fa fa-github'
}
]
// menu items
this.menuItems = allItems.filter(
(item) => {
if (menuItems) return menuItems.find((name) => { return name === item.action })
}
)
self._components = {} self._components = {}
self._components.registry = localRegistry || globalRegistry self._components.registry = localRegistry || globalRegistry
...@@ -122,13 +154,19 @@ function fileExplorer (localRegistry, files) { ...@@ -122,13 +154,19 @@ function fileExplorer (localRegistry, files) {
}, },
formatSelf: function formatSelf (key, data, li) { formatSelf: function formatSelf (key, data, li) {
var isRoot = data.path === self.files.type var isRoot = data.path === self.files.type
return yo`<label return yo`
<div class="${css.items}">
<label
class=${css.label} class=${css.label}
data-path="${data.path}" data-path="${data.path}"
style="${isRoot ? 'font-weight:bold;' : ''}" style="${isRoot ? 'font-weight:bold;' : ''}"
onkeydown=${editModeOff} onkeydown=${editModeOff}
onblur=${editModeOff} onblur=${editModeOff}
>${key.split('/').pop()}</label>` >${key.split('/').pop()}
</label>
${isRoot ? self.renderMenuItems() : ''}
</div>
`
} }
}) })
...@@ -276,6 +314,218 @@ fileExplorer.prototype.init = function () { ...@@ -276,6 +314,218 @@ fileExplorer.prototype.init = function () {
return this.container return this.container
} }
fileExplorer.prototype.publishToGist = function () {
modalDialogCustom.confirm(
null,
'Are you sure you want to publish all your files anonymously as a public gist on github.com?',
() => { this.toGist() }
)
}
fileExplorer.prototype.uploadFile = function (event) {
// TODO The file explorer is merely a view on the current state of
// the files module. Please ask the user here if they want to overwrite
// a file and then just use `files.add`. The file explorer will
// pick that up via the 'fileAdded' event from the files module.
let self = this
;[...event.target.files].forEach((file) => {
let files = this.files
function loadFile () {
var fileReader = new FileReader()
fileReader.onload = function (event) {
if (helper.checkSpecialChars(file.name)) {
modalDialogCustom.alert('Special characters are not allowed')
return
}
var success = files.set(name, event.target.result)
if (!success) {
modalDialogCustom.alert('Failed to create file ' + name)
} else {
self.events.trigger('focus', [name])
}
}
fileReader.readAsText(file)
}
var name = files.type + '/' + file.name
files.exists(name, (error, exist) => {
if (error) console.log(error)
if (!exist) {
loadFile()
} else {
modalDialogCustom.confirm(null, `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() })
}
})
})
}
fileExplorer.prototype.toGist = function (id) {
let proccedResult = function (error, data) {
if (error) {
modalDialogCustom.alert('Failed to manage gist: ' + error)
} else {
if (data.html_url) {
modalDialogCustom.confirm(null, `The gist is at ${data.html_url}. Would you like to open it in a new window?`, () => {
window.open(data.html_url, '_blank')
})
} else {
modalDialogCustom.alert(data.message + ' ' + data.documentation_url + ' ' + JSON.stringify(data.errors, null, '\t'))
}
}
}
this.packageFiles(this.files, (error, packaged) => {
if (error) {
console.log(error)
modalDialogCustom.alert('Failed to create gist: ' + error)
} else {
var tokenAccess = this._deps.config.get('settings/gist-access-token')
if (!tokenAccess) {
modalDialogCustom.alert(
'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.'
)
} else {
var description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' +
queryParams.get().version +
'&optimize=' +
queryParams.get().optimize +
'&gist='
var gists = new Gists({
token: tokenAccess
})
if (id) {
tooltip('Saving gist (' + id + ') ...')
gists.edit({
description: description,
public: true,
files: packaged,
id: id
}, (error, result) => {
proccedResult(error, result)
})
} else {
tooltip('Creating a new gist ...')
gists.create({
description: description,
public: true,
files: packaged
}, (error, result) => {
proccedResult(error, result)
})
}
}
}
})
}
// return all the files, except the temporary/readonly ones..
fileExplorer.prototype.packageFiles = function (filesProvider, callback) {
var ret = {}
filesProvider.resolveDirectory(filesProvider.type, (error, files) => {
if (error) callback(error)
else {
async.eachSeries(Object.keys(files), (path, cb) => {
filesProvider.get(path, (error, content) => {
if (/^\s+$/.test(content)) {
content = '// this line is added to create a gist. Empty file is not allowed.'
}
if (error) cb(error)
else {
ret[path] = { content }
cb()
}
})
}, (error) => {
callback(error, ret)
})
}
})
}
// ------------------ copy files --------------
fileExplorer.prototype.copyFiles = function () {
let self = this
modalDialogCustom.prompt(
null,
'To which other remix-ide instance do you want to copy over all files?',
'https://remix.ethereum.org',
(target) => {
doCopy(target)
}
)
function doCopy (target) {
// package only files from the browser storage.
self.packageFiles(self.files, (error, packaged) => {
if (error) {
console.log(error)
} else {
let iframe = yo`
<iframe src=${target} style='display:none;'></iframe>
`
iframe.onload = function () {
iframe.contentWindow.postMessage(['loadFiles', packaged], '*')
tooltip('Files copied successfully.')
}
document.querySelector('body').appendChild(iframe)
}
})
}
}
// ------------------ gist publish --------------
fileExplorer.prototype.updateGist = function () {
var gistId = this.files.id
if (!gistId) {
tooltip('no gist content is currently loaded.')
} else {
this.toGist(gistId)
}
}
fileExplorer.prototype.createNewFile = function () {
let self = this
modalDialogCustom.prompt(null, 'File Name', 'Untitled.sol', (input) => {
helper.createNonClashingName(input, self.files, (error, newName) => {
if (error) return modalDialogCustom.alert('Failed to create file ' + newName + ' ' + error)
if (!self.files.set(newName, '')) {
modalDialogCustom.alert('Failed to create file ' + newName)
} else {
var file = self.files.type + '/' + newName
self._deps.fileManager.switchFile(file)
if (file.includes('_test.sol')) {
self.event.trigger('newTestFileCreated', [file])
}
}
})
}, null, true)
}
fileExplorer.prototype.renderMenuItems = function () {
let items = ''
if (this.menuItems) {
items = this.menuItems.map(({action, title, icon}) => {
if (action === 'uploadFile') {
return yo`
<label class="${icon} ${css.newFile}">
<input type="file" onchange=${(event) => {
event.stopPropagation()
this.uploadFile(event)
}} multiple />
</label>
`
} else {
return yo`
<span onclick=${(event) => { event.stopPropagation(); this[ action ]() }} class="newFile ${css.newFile}" title=${title}>
<i class=${icon}></i>
</span>
`
}
})
}
return yo`<span class=${css.menu}>${items}</span>`
}
fileExplorer.prototype.ensureRoot = function (cb) { fileExplorer.prototype.ensureRoot = function (cb) {
cb = cb || (() => {}) cb = cb || (() => {})
var self = this var self = this
......
...@@ -3,14 +3,16 @@ ...@@ -3,14 +3,16 @@
const EventEmitter = require('events') const EventEmitter = require('events')
var globalRegistry = require('../../global/registry') var globalRegistry = require('../../global/registry')
var CompilerImport = require('../compiler/compiler-imports') var CompilerImport = require('../compiler/compiler-imports')
import { ApiFactory } from 'remix-plugin'
/* /*
attach to files event (removed renamed) attach to files event (removed renamed)
trigger: currentFileChanged trigger: currentFileChanged
*/ */
class FileManager { class FileManager extends ApiFactory {
constructor (localRegistry) { constructor (localRegistry) {
super()
this.openedFiles = {} // list all opened files this.openedFiles = {} // list all opened files
this.events = new EventEmitter() this.events = new EventEmitter()
this._components = {} this._components = {}
...@@ -19,30 +21,29 @@ class FileManager { ...@@ -19,30 +21,29 @@ class FileManager {
} }
init () { init () {
var self = this this._deps = {
self._deps = { editor: this._components.registry.get('editor').api,
editor: self._components.registry.get('editor').api, config: this._components.registry.get('config').api,
config: self._components.registry.get('config').api, browserExplorer: this._components.registry.get('fileproviders/browser').api,
browserExplorer: self._components.registry.get('fileproviders/browser').api, localhostExplorer: this._components.registry.get('fileproviders/localhost').api,
localhostExplorer: self._components.registry.get('fileproviders/localhost').api, configExplorer: this._components.registry.get('fileproviders/config').api,
configExplorer: self._components.registry.get('fileproviders/config').api, gistExplorer: this._components.registry.get('fileproviders/gist').api,
gistExplorer: self._components.registry.get('fileproviders/gist').api, filesProviders: this._components.registry.get('fileproviders').api
filesProviders: self._components.registry.get('fileproviders').api
} }
self._deps.browserExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) this._deps.browserExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
self._deps.localhostExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) this._deps.localhostExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
self._deps.configExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) this._deps.configExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
self._deps.gistExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) this._deps.gistExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
self._deps.browserExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) }) this._deps.browserExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
self._deps.localhostExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) }) this._deps.localhostExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
self._deps.configExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) }) this._deps.configExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
self._deps.gistExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) }) this._deps.gistExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
self._deps.localhostExplorer.event.register('errored', (event) => { this.removeTabsOf(self._deps.localhostExplorer) }) this._deps.localhostExplorer.event.register('errored', (event) => { this.removeTabsOf(this._deps.localhostExplorer) })
self._deps.localhostExplorer.event.register('closed', (event) => { this.removeTabsOf(self._deps.localhostExplorer) }) this._deps.localhostExplorer.event.register('closed', (event) => { this.removeTabsOf(this._deps.localhostExplorer) })
} }
profile () { get profile () {
return { return {
displayName: 'file manager', displayName: 'file manager',
name: 'fileManager', name: 'fileManager',
...@@ -53,10 +54,9 @@ class FileManager { ...@@ -53,10 +54,9 @@ class FileManager {
} }
fileRenamedEvent (oldName, newName, isFolder) { fileRenamedEvent (oldName, newName, isFolder) {
var self = this
if (!isFolder) { if (!isFolder) {
self._deps.config.set('currentFile', '') this._deps.config.set('currentFile', '')
self._deps.editor.discard(oldName) this._deps.editor.discard(oldName)
if (this.openedFiles[oldName]) { if (this.openedFiles[oldName]) {
delete this.openedFiles[oldName] delete this.openedFiles[oldName]
this.openedFiles[newName] = newName this.openedFiles[newName] = newName
...@@ -69,7 +69,7 @@ class FileManager { ...@@ -69,7 +69,7 @@ class FileManager {
var newAbsolutePath = k.replace(oldName, newName) var newAbsolutePath = k.replace(oldName, newName)
this.openedFiles[newAbsolutePath] = newAbsolutePath this.openedFiles[newAbsolutePath] = newAbsolutePath
delete this.openedFiles[k] delete this.openedFiles[k]
if (self._deps.config.get('currentFile') === k) { if (this._deps.config.get('currentFile') === k) {
newFocus = newAbsolutePath newFocus = newAbsolutePath
} }
} }
...@@ -105,8 +105,7 @@ class FileManager { ...@@ -105,8 +105,7 @@ class FileManager {
} }
currentPath () { currentPath () {
var self = this var currentFile = this._deps.config.get('currentFile')
var currentFile = self._deps.config.get('currentFile')
var reg = /(.*)(\/).*/ var reg = /(.*)(\/).*/
var path = reg.exec(currentFile) var path = reg.exec(currentFile)
return path ? path[1] : null return path ? path[1] : null
...@@ -154,57 +153,55 @@ class FileManager { ...@@ -154,57 +153,55 @@ class FileManager {
} }
fileRemovedEvent (path) { fileRemovedEvent (path) {
var self = this
if (!this.openedFiles[path]) return if (!this.openedFiles[path]) return
if (path === self._deps.config.get('currentFile')) { if (path === this._deps.config.get('currentFile')) {
self._deps.config.set('currentFile', '') this._deps.config.set('currentFile', '')
} }
self._deps.editor.discard(path) this._deps.editor.discard(path)
delete this.openedFiles[path] delete this.openedFiles[path]
this.events.emit('fileRemoved', path) this.events.emit('fileRemoved', path)
this.switchFile() this.switchFile()
} }
switchFile (file) { switchFile (file) {
var self = this const _switchFile = (file) => {
this.saveCurrentFile()
this._deps.config.set('currentFile', file)
this.openedFiles[file] = file
this.fileProviderOf(file).get(file, (error, content) => {
if (error) {
console.log(error)
} else {
if (this.fileProviderOf(file).isReadOnly(file)) {
this._deps.editor.openReadOnly(file, content)
} else {
this._deps.editor.open(file, content)
}
this.events.emit('currentFileChanged', file)
}
})
}
if (file) return _switchFile(file) if (file) return _switchFile(file)
else { else {
var browserProvider = self._deps.filesProviders['browser'] var browserProvider = this._deps.filesProviders['browser']
browserProvider.resolveDirectory('browser', (error, filesTree) => { browserProvider.resolveDirectory('browser', (error, filesTree) => {
if (error) console.error(error) if (error) console.error(error)
var fileList = Object.keys(filesTree) var fileList = Object.keys(filesTree)
if (fileList.length) { if (fileList.length) {
_switchFile(browserProvider.type + '/' + fileList[0]) _switchFile(browserProvider.type + '/' + fileList[0])
} else { } else {
self.events.emit('currentFileChanged') this.events.emit('currentFileChanged')
self._deps.editor.displayEmptyReadOnlySession() this._deps.editor.displayEmptyReadOnlySession()
}
})
}
function _switchFile (file) {
self.saveCurrentFile()
self._deps.config.set('currentFile', file)
self.openedFiles[file] = file
self.fileProviderOf(file).get(file, (error, content) => {
if (error) {
console.log(error)
} else {
if (self.fileProviderOf(file).isReadOnly(file)) {
self._deps.editor.openReadOnly(file, content)
} else {
self._deps.editor.open(file, content)
}
self.events.emit('currentFileChanged', file)
} }
}) })
} }
} }
getFilesFromPath (path) { getFilesFromPath (path) {
const provider = this.fileProviderOf(path)
if (!provider) throw new Error(`provider for path ${path} not found`)
// TODO : Change provider with promise // TODO : Change provider with promise
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const provider = this.fileProviderOf(path)
if (!provider) return reject(`provider for path ${path} not found`)
provider.resolveDirectory(path, (error, filesTree) => { provider.resolveDirectory(path, (error, filesTree) => {
if (error) reject(error) if (error) reject(error)
resolve(filesTree) resolve(filesTree)
...@@ -243,7 +240,6 @@ class FileManager { ...@@ -243,7 +240,6 @@ class FileManager {
} }
syncEditor (path) { syncEditor (path) {
var self = this
var currentFile = this._deps.config.get('currentFile') var currentFile = this._deps.config.get('currentFile')
if (path !== currentFile) return if (path !== currentFile) return
...@@ -251,7 +247,7 @@ class FileManager { ...@@ -251,7 +247,7 @@ class FileManager {
if (provider) { if (provider) {
provider.get(currentFile, (error, content) => { provider.get(currentFile, (error, content) => {
if (error) console.log(error) if (error) console.log(error)
self._deps.editor.setText(content) this._deps.editor.setText(content)
}) })
} else { } else {
console.log('cannot save ' + currentFile + '. Does not belong to any explorer') console.log('cannot save ' + currentFile + '. Does not belong to any explorer')
......
import { ApiFactory } from 'remix-plugin'
var yo = require('yo-yo') var yo = require('yo-yo')
var modalDialog = require('../ui/modaldialog') var modalDialog = require('../ui/modaldialog')
...@@ -14,13 +16,14 @@ var css = csjs` ...@@ -14,13 +16,14 @@ var css = csjs`
} }
` `
export class RemixdHandle { export class RemixdHandle extends ApiFactory {
constructor (fileSystemExplorer, locahostProvider) { constructor (fileSystemExplorer, locahostProvider) {
super()
this.fileSystemExplorer = fileSystemExplorer this.fileSystemExplorer = fileSystemExplorer
this.locahostProvider = locahostProvider this.locahostProvider = locahostProvider
} }
profile () { get profile () {
return { return {
name: 'remixd', name: 'remixd',
methods: [], methods: [],
......
...@@ -16,6 +16,22 @@ var css = csjs` ...@@ -16,6 +16,22 @@ var css = csjs`
cursor : pointer; cursor : pointer;
} }
.file { .file {
padding : 4px;
}
.newFile {
padding : 4px;
}
.newFile i {
cursor : pointer;
}
.newFile i:hover {
color : var(--secondary)
}
.menu {
margin-left : 20px;
}
.items {
display : inline
} }
.hasFocus { .hasFocus {
} }
......
This diff is collapsed.
...@@ -14,22 +14,6 @@ var css = csjs` ...@@ -14,22 +14,6 @@ var css = csjs`
position : relative; position : relative;
width : 100%; width : 100%;
} }
.menu {
margin-top : -0.2em;
flex-shrink : 0;
display : flex;
flex-direction : row;
min-width : 160px;
}
.newFile {
padding : 10px;
}
.newFile i {
cursor : pointer;
}
.newFile i:hover {
color : var(--secondary)
}
.gist { .gist {
padding : 10px; padding : 10px;
} }
......
...@@ -52,13 +52,15 @@ export class TabProxy { ...@@ -52,13 +52,15 @@ export class TabProxy {
appStore.event.on('activate', (name) => { appStore.event.on('activate', (name) => {
const { profile } = appStore.getOne(name) const { profile } = appStore.getOne(name)
if (profile.prefferedLocation === 'mainPanel') { if (profile.location === 'mainPanel') {
this.addTab(name, () => { this.addTab(
this.event.emit('switchApp', name) name,
}, () => { () => this.event.emit('switchApp', name),
() => {
this.event.emit('closeApp', name) this.event.emit('closeApp', name)
this.appManager.deactivateOne(name) this.appManager.deactivateOne(name)
}) }
)
} }
}) })
......
...@@ -13,7 +13,6 @@ var CommandInterpreterAPI = require('../../lib/cmdInterpreterAPI') ...@@ -13,7 +13,6 @@ var CommandInterpreterAPI = require('../../lib/cmdInterpreterAPI')
var executionContext = require('../../execution-context') var executionContext = require('../../execution-context')
var Dropdown = require('../ui/dropdown') var Dropdown = require('../ui/dropdown')
var AutoCompletePopup = require('../ui/auto-complete-popup') var AutoCompletePopup = require('../ui/auto-complete-popup')
var Commands = require('../constants/commands')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
...@@ -63,17 +62,12 @@ class Terminal { ...@@ -63,17 +62,12 @@ class Terminal {
}) })
self._components.autoCompletePopup = new AutoCompletePopup() self._components.autoCompletePopup = new AutoCompletePopup()
self._components.autoCompletePopup.event.register('handleSelect', function (input) { self._components.autoCompletePopup.event.register('handleSelect', function (input) {
self._components.autoCompletePopup.data._options = []
self._components.autoCompletePopup._startingElement = 0
let textList = self._view.input.innerText.split(' ') let textList = self._view.input.innerText.split(' ')
textList.pop() textList.pop()
textList.push(input) textList.push(input)
self._view.input.innerText = `${textList}`.replace(/,/g, ' ') self._view.input.innerText = `${textList}`.replace(/,/g, ' ')
self._view.input.focus() self._view.input.focus()
yo.update(self._view.autoCompletePopup, self._components.autoCompletePopup.render()) self.putCursor2End(self._view.input)
})
self._components.autoCompletePopup.event.register('updateList', function () {
yo.update(self._view.autoCompletePopup, self._components.autoCompletePopup.render())
}) })
self._commands = {} self._commands = {}
self.commands = {} self.commands = {}
...@@ -115,7 +109,7 @@ class Terminal { ...@@ -115,7 +109,7 @@ class Terminal {
if (self._view.el) return self._view.el if (self._view.el) return self._view.el
self._view.journal = yo`<div class=${css.journal}></div>` self._view.journal = yo`<div class=${css.journal}></div>`
self._view.input = yo` self._view.input = yo`
<span class=${css.input} contenteditable="true" onpaste=${paste} onkeydown=${change}></span> <span class=${css.input} spellcheck="false" contenteditable="true" onpaste=${paste} onkeydown=${change}></span>
` `
self._view.input.innerText = '\n' self._view.input.innerText = '\n'
self._view.cli = yo` self._view.cli = yo`
...@@ -150,7 +144,7 @@ class Terminal { ...@@ -150,7 +144,7 @@ class Terminal {
${self._view.dropdown} ${self._view.dropdown}
<div class=${css.search}> <div class=${css.search}>
<i class="fa fa-search ${css.searchIcon} bg-light btn" aria-hidden="true"></i> <i class="fa fa-search ${css.searchIcon} bg-light btn" aria-hidden="true"></i>
<input type="text" class=${css.filter} onkeydown=${filter} placeholder="Search transactions"> <input spellcheck="false" type="text" class=${css.filter} onkeydown=${filter} placeholder="Search transactions">
</div> </div>
</div> </div>
</div> </div>
...@@ -163,7 +157,6 @@ class Terminal { ...@@ -163,7 +157,6 @@ class Terminal {
</div> </div>
</div> </div>
` `
self._view.autoCompletePopup = self._components.autoCompletePopup.render()
self._view.el = yo` self._view.el = yo`
<div class="${css.panel}"> <div class="${css.panel}">
${self._view.bar} ${self._view.bar}
...@@ -406,14 +399,16 @@ class Terminal { ...@@ -406,14 +399,16 @@ class Terminal {
return self._view.el return self._view.el
function change (event) { function change (event) {
handleAutoComplete(event) if (self._components.autoCompletePopup.handleAutoComplete(
event,
self._view.input.innerText)) return
if (self._view.input.innerText.length === 0) self._view.input.innerText += '\n' if (self._view.input.innerText.length === 0) self._view.input.innerText += '\n'
if (event.which === 13) { if (event.which === 13) {
if (event.ctrlKey) { // <ctrl+enter> if (event.ctrlKey) { // <ctrl+enter>
self._view.input.innerText += '\n' self._view.input.innerText += '\n'
putCursor2End(self._view.input) self.putCursor2End(self._view.input)
self.scroll2bottom() self.scroll2bottom()
removeAutoComplete() self._components.autoCompletePopup.removeAutoComplete()
} else { // <enter> } else { // <enter>
self._cmdIndex = -1 self._cmdIndex = -1
self._cmdTemp = '' self._cmdTemp = ''
...@@ -424,37 +419,30 @@ class Terminal { ...@@ -424,37 +419,30 @@ class Terminal {
self._cmdHistory.unshift(script) self._cmdHistory.unshift(script)
self.commands.script(script) self.commands.script(script)
} }
removeAutoComplete() self._components.autoCompletePopup.removeAutoComplete()
} }
} else if (event.which === 38) { // <arrowUp> } else if (event.which === 38) { // <arrowUp>
if (self._components.autoCompletePopup.data._options.length > self._components.autoCompletePopup._elementsToShow) {
self._components.autoCompletePopup._view.autoComplete.children[1].children[0].onclick(event)
} else {
var len = self._cmdHistory.length var len = self._cmdHistory.length
if (len === 0) return event.preventDefault() if (len === 0) return event.preventDefault()
if (self._cmdHistory.length - 1 > self._cmdIndex) { if (self._cmdHistory.length - 1 > self._cmdIndex) {
self._cmdIndex++ self._cmdIndex++
} }
self._view.input.innerText = self._cmdHistory[self._cmdIndex] self._view.input.innerText = self._cmdHistory[self._cmdIndex]
putCursor2End(self._view.input) self.putCursor2End(self._view.input)
self.scroll2bottom() self.scroll2bottom()
}
} else if (event.which === 40) { // <arrowDown> } else if (event.which === 40) { // <arrowDown>
if (self._components.autoCompletePopup.data._options.length > self._components.autoCompletePopup._elementsToShow) {
self._components.autoCompletePopup._view.autoComplete.children[1].children[1].onclick(event)
} else {
if (self._cmdIndex > -1) { if (self._cmdIndex > -1) {
self._cmdIndex-- self._cmdIndex--
} }
self._view.input.innerText = self._cmdIndex >= 0 ? self._cmdHistory[self._cmdIndex] : self._cmdTemp self._view.input.innerText = self._cmdIndex >= 0 ? self._cmdHistory[self._cmdIndex] : self._cmdTemp
putCursor2End(self._view.input) self.putCursor2End(self._view.input)
self.scroll2bottom() self.scroll2bottom()
}
} else { } else {
self._cmdTemp = self._view.input.innerText self._cmdTemp = self._view.input.innerText
} }
} }
function putCursor2End (editable) { }
putCursor2End (editable) {
var range = document.createRange() var range = document.createRange()
range.selectNode(editable) range.selectNode(editable)
var child = editable var child = editable
...@@ -481,46 +469,6 @@ class Terminal { ...@@ -481,46 +469,6 @@ class Terminal {
editable.focus() editable.focus()
} }
function handleAutoComplete (event) {
if (event.which === 9) {
event.preventDefault()
let textList = self._view.input.innerText.split(' ')
let autoCompleteInput = textList.length > 1 ? textList[textList.length - 1] : textList[0]
if (self._view.input.innerText.length >= 2) {
self._components.autoCompletePopup.data._options = []
Commands.allPrograms.forEach(item => {
if (Object.keys(item)[0].substring(0, Object.keys(item)[0].length - 1).includes(autoCompleteInput.trim())) {
self._components.autoCompletePopup.data._options.push(item)
} else if (autoCompleteInput.trim().includes(Object.keys(item)[0]) || (Object.keys(item)[0] === autoCompleteInput.trim())) {
Commands.allCommands.forEach(item => {
if (Object.keys(item)[0].includes(autoCompleteInput.trim())) {
self._components.autoCompletePopup.data._options.push(item)
}
})
}
})
}
if (self._components.autoCompletePopup.data._options.length === 1) {
textList.pop()
textList.push(Object.keys(self._components.autoCompletePopup.data._options[0])[0])
self._view.input.innerText = `${textList}`.replace(/,/g, ' ')
self._components.autoCompletePopup.data._options = []
putCursor2End(self._view.input)
}
}
if (event.which === 27 || event.which === 8 || event.which === 46) {
self._components.autoCompletePopup.data._options = []
self._components.autoCompletePopup._startingElement = 0
}
yo.update(self._view.autoCompletePopup, self._components.autoCompletePopup.render())
}
function removeAutoComplete () {
self._components.autoCompletePopup.data._options = []
self._components.autoCompletePopup._startingElement = 0
self._components.autoCompletePopup._removePopUp()
yo.update(self._view.autoCompletePopup, self._components.autoCompletePopup.render())
}
}
updateJournal (filterEvent) { updateJournal (filterEvent) {
var self = this var self = this
var commands = self.data.activeFilters.commands var commands = self.data.activeFilters.commands
......
...@@ -3,13 +3,17 @@ var StaticAnalysis = require('../staticanalysis/staticAnalysisView') ...@@ -3,13 +3,17 @@ var StaticAnalysis = require('../staticanalysis/staticAnalysisView')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var css = require('./styles/analysis-tab-styles') var css = require('./styles/analysis-tab-styles')
class AnalysisTab { import { ApiFactory } from 'remix-plugin'
class AnalysisTab extends ApiFactory {
constructor (registry) { constructor (registry) {
super()
this.event = new EventManager() this.event = new EventManager()
this.registry = registry this.registry = registry
} }
profile () {
get profile () {
return { return {
name: 'solidityStaticAnalysis', name: 'solidityStaticAnalysis',
displayName: 'solidity static analysis', displayName: 'solidity static analysis',
...@@ -20,6 +24,7 @@ class AnalysisTab { ...@@ -20,6 +24,7 @@ class AnalysisTab {
kind: 'analysis' kind: 'analysis'
} }
} }
render () { render () {
var staticanalysis = new StaticAnalysis() var staticanalysis = new StaticAnalysis()
this.registry.put({api: staticanalysis, name: 'staticanalysis'}) this.registry.put({api: staticanalysis, name: 'staticanalysis'})
...@@ -28,6 +33,7 @@ class AnalysisTab { ...@@ -28,6 +33,7 @@ class AnalysisTab {
this.el = yo`<div class="${css.analysisTabView}" id="staticanalysisView">${staticanalysis.render()}</div>` this.el = yo`<div class="${css.analysisTabView}" id="staticanalysisView">${staticanalysis.render()}</div>`
return this.el return this.el
} }
} }
module.exports = AnalysisTab module.exports = AnalysisTab
This diff is collapsed.
...@@ -3,12 +3,16 @@ var css = require('./styles/debugger-tab-styles') ...@@ -3,12 +3,16 @@ var css = require('./styles/debugger-tab-styles')
var DebuggerUI = require('../debugger/debuggerUI') var DebuggerUI = require('../debugger/debuggerUI')
class DebuggerTab { import { ApiFactory } from 'remix-plugin'
class DebuggerTab extends ApiFactory {
constructor () { constructor () {
super()
this.el = null this.el = null
} }
profile () { get profile () {
return { return {
displayName: 'debugger', displayName: 'debugger',
name: 'debugger', name: 'debugger',
......
...@@ -12,9 +12,12 @@ var ContractDropdownUI = require('./runTab/contractDropdown.js') ...@@ -12,9 +12,12 @@ var ContractDropdownUI = require('./runTab/contractDropdown.js')
var Recorder = require('./runTab/model/recorder.js') var Recorder = require('./runTab/model/recorder.js')
var RecorderUI = require('./runTab/recorder.js') var RecorderUI = require('./runTab/recorder.js')
class RunTab { import { ApiFactory } from 'remix-plugin'
class RunTab extends ApiFactory {
constructor (udapp, udappUI, config, fileManager, editor, logCallback, filePanel, pluginManager, compilersArtefacts) { constructor (udapp, udappUI, config, fileManager, editor, logCallback, filePanel, pluginManager, compilersArtefacts) {
super()
this.event = new EventManager() this.event = new EventManager()
this.renderInstanceContainer() this.renderInstanceContainer()
...@@ -25,6 +28,18 @@ class RunTab { ...@@ -25,6 +28,18 @@ class RunTab {
this.renderContainer() this.renderContainer()
} }
get profile () {
return {
name: 'run',
displayName: 'run transactions',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNTc2IDkyN2wtMTMyOCA3MzhxLTIzIDEzLTM5LjUgM3QtMTYuNS0zNnYtMTQ3MnEwLTI2IDE2LjUtMzZ0MzkuNSAzbDEzMjggNzM4cTIzIDEzIDIzIDMxdC0yMyAzMXoiLz48L3N2Zz4=',
description: 'execute and save transactions',
kind: 'run'
}
}
renderContainer () { renderContainer () {
this.container = yo`<div class="${css.runTabView}" id="runTabView" ></div>` this.container = yo`<div class="${css.runTabView}" id="runTabView" ></div>`
...@@ -147,17 +162,6 @@ class RunTab { ...@@ -147,17 +162,6 @@ class RunTab {
return this.container return this.container
} }
profile () {
return {
name: 'run',
displayName: 'run transactions',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNTc2IDkyN2wtMTMyOCA3MzhxLTIzIDEzLTM5LjUgM3QtMTYuNS0zNnYtMTQ3MnEwLTI2IDE2LjUtMzZ0MzkuNSAzbDEzMjggNzM4cTIzIDEzIDIzIDMxdC0yMyAzMXoiLz48L3N2Zz4=',
description: 'execute and save transactions',
kind: 'run'
}
}
} }
module.exports = RunTab module.exports = RunTab
This diff is collapsed.
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var css = csjs` const css = csjs`
.settingsTabView { .settingsTabView {
padding: 2%; padding: 2%;
display: flex;
} }
.info { .info {
margin-bottom: 1em; margin-bottom: .6rem;
word-break: break-word; word-break: break-word;
font-size: .8rem;
}
.info h7 {
margin-bottom: .5rem;
} }
.title { .title {
font-size: 1.1em; // font-size: 1.1em;
font-weight: bold; // font-weight: bold;
margin-bottom: 1em; // margin-bottom: 1em;
}
.frow {
margin-bottom: .5rem;
} }
.crow { .crow {
display: flex; // display: flex;
overflow: auto; // overflow: auto;
clear: both; // clear: both;
padding: .2em; // padding: .2em;
} }
.checkboxText { .checkboxText {
font-weight: normal; font-weight: normal;
...@@ -35,10 +41,6 @@ var css = csjs` ...@@ -35,10 +41,6 @@ var css = csjs`
padding: .5em; padding: .5em;
font-weight: bold; font-weight: bold;
} }
.select {
font-weight: bold;
margin-top: 1em;
}
.heading { .heading {
margin-bottom: 0; margin-bottom: 0;
} }
...@@ -49,6 +51,7 @@ var css = csjs` ...@@ -49,6 +51,7 @@ var css = csjs`
input { input {
margin-right: 5px; margin-right: 5px;
cursor: pointer; cursor: pointer;
width: inherit;
} }
input[type=radio] { input[type=radio] {
margin-top: 2px; margin-top: 2px;
...@@ -56,24 +59,36 @@ var css = csjs` ...@@ -56,24 +59,36 @@ var css = csjs`
.pluginTextArea { .pluginTextArea {
font-family: unset; font-family: unset;
} }
.pluginLoad {
vertical-align: top; .removePlugin {
} cursor: pointer;
i.warnIt {
color: var(--warning);
} }
.icon { .icon {
margin-right: .5em; margin-right: .5em;
} }
.remixdinstallation {
padding: 3px;
border-radius: 2px;
margin-left: 5px;
}
.savegisttoken { .savegisttoken {
margin-left: 5px; margin-left: 5px;
} }
} .aPlugin {
display: inline-block;
padding-left: 10px;
padding-top: 4px;
padding-bottom: 6px;
max-width: 100px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
vertical-align: middle;
}
.removePlugin{
padding-left: 7px;
padding-right: 7px;
margin-left: 10px;
}
.inline {
display: inline;
width: 50%;
}
` `
module.exports = css module.exports = css
const csjs = require('csjs-inject')
const css = csjs`
.menu {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.active {
}
.options {
float: left;
padding-top: 0.7em;
min-width: 60px;
font-size: 0.9em;
cursor: pointer;
font-size: 1em;
text-align: center;
}
.optionViews {
overflow: scroll;
height: 100%;
}
.optionViews > div {
display: none;
}
.optionViews .pre {
word-wrap: break-word;
border-radius: 3px;
display: inline-block;
padding: 0 0.6em;
}
`
module.exports = css
const yo = require('yo-yo')
var css = require('./styles/support-tab-styles')
class SupportTab {
constructor (localRegistry) {
this.el = null
this.gitterIframe = ''
this.gitterIsLoaded = false
}
__showing () {
if (this.gitterIsLoaded) return
const iframe = yo`<iframe class="${css.chatIframe}" src='https://gitter.im/ethereum/remix/~embed'></iframe>`
this.gitterIframe.parentNode.replaceChild(iframe, this.gitterIframe)
this.gitterIframe = iframe
this.el.style.display = 'block'
this.gitterIsLoaded = true
}
profile () {
return {
name: 'support',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik03MDQgMTkycTAtMjYtMTktNDV0LTQ1LTE5LTQ1IDE5LTE5IDQ1IDE5IDQ1IDQ1IDE5IDQ1LTE5IDE5LTQ1em04OTYtMzJ2MzIwcTAgMTYtMTIgMjUtOCA3LTIwIDctNCAwLTctMWwtNDQ4LTk2cS0xMS0yLTE4LTExdC03LTIwaC0yNTZ2MTAycTExMSAyMyAxODMuNSAxMTF0NzIuNSAyMDN2ODAwcTAgMjYtMTkgNDV0LTQ1IDE5aC01MTJxLTI2IDAtNDUtMTl0LTE5LTQ1di04MDBxMC0xMDYgNjIuNS0xOTAuNXQxNjEuNS0xMTQuNXYtMTExaC0zMnEtNTkgMC0xMTUgMjMuNXQtOTEuNSA1My02NiA2Ni41LTQwLjUgNTMuNS0xNCAyNC41cS0xNyAzNS01NyAzNS0xNiAwLTI5LTctMjMtMTItMzEuNS0zN3QzLjUtNDlxNS0xMCAxNC41LTI2dDM3LjUtNTMuNSA2MC41LTcwIDg1LTY3IDEwOC41LTUyLjVxLTI1LTQyLTI1LTg2IDAtNjYgNDctMTEzdDExMy00NyAxMTMgNDcgNDcgMTEzcTAgMzMtMTQgNjRoMzAycTAtMTEgNy0yMHQxOC0xMWw0NDgtOTZxMy0xIDctMSAxMiAwIDIwIDcgMTIgOSAxMiAyNXoiLz48L3N2Zz4=',
description: 'help center'
}
}
render () {
if (this.el) return this.el
this.gitterIframe = yo`<div></div>`
const remixd = yo`
<div class="${css.info}">
<div class=${css.title}>Accessing local files</div>
<div class="${css.crow}">
Remixd is a tool which allow Remix IDE to access files located in your local computer.
it can also be used to setup a development environment.
</div>
<div class="${css.crow}">More infos:</div>
<div class="${css.crow}"><a target="_blank" href="https://github.com/ethereum/remixd"> https://github.com/ethereum/remixd</a></div>
<div class="${css.crow}"><a target="_blank" href="https://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem">http://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html</a></div>
<div class="${css.crow}">Installation: <pre class=${css.remixdinstallation}>npm install remixd -g</pre></div>
</div>`
const localremixd = yo`
<div class="${css.info}">
<div class=${css.title}>Running Remix locally</div>
<div class="${css.crow}">
as a NPM module:
</div>
<a target="_blank" href="https://www.npmjs.com/package/remix-ide">https://www.npmjs.com/package/remix-ide</a>
<pre class=${css.remixdinstallation}>npm install remix-ide -g</pre>
<div class="${css.crow}">
as an electron app:
</div>
<a target="_blank" href="https://github.com/horizon-games/remix-app">https://github.com/horizon-games/remix-app</a>
</div>`
this.el = yo`
<div class="${css.supportTabView}" id="supportView">
<div class="${css.infoBox}">
Have a question, found a bug or want to propose a feature? Have a look at the
<a target="_blank" href='https://github.com/ethereum/remix-ide/issues'> issues</a> or check out
<a target="_blank" href='https://remix.readthedocs.io/en/latest/'> the documentation page on Remix</a> or
<a target="_blank" href='https://solidity.readthedocs.io/en/latest/'> Solidity</a>.
</div>
<div class="${css.chat}">
<div class="${css.chatTitle}" onclick=${() => { window.open('https://gitter.im/ethereum/remix') }} title='Click to open chat in Gitter'>
<div class="${css.chatTitleText}">ethereum/remix community chat</div>
</div>
${this.gitterIframe}
</div>
${remixd}
${localremixd}
</div>`
return this.el
}
}
module.exports = SupportTab
var yo = require('yo-yo')
var css = require('./styles/tabbed-menu-styles')
var globalRegistry = require('../../global/registry')
var helper = require('../../lib/helper')
var EventManager = require('../../lib/events')
class TabbedMenu {
constructor (localRegistry) {
const self = this
self.event = new EventManager()
self._components = {}
self._components.registry = localRegistry || globalRegistry
self._deps = {
app: self._components.registry.get('app').api
}
self._view = { el: null, viewport: null, tabs: {}, contents: {} }
}
render () {
const self = this
if (self._view.el) return self._view.el
self._view.el = yo`<ul class=${css.menu}>${Object.values(self._view.tabs)}</ul>`
return self._view.el
}
renderViewport () {
const self = this
if (self._view.viewport) return self._view.viewport
self._view.viewport = yo`
<div id="optionViews" class=${css.optionViews}>
${Object.values(self._view.contents)}
</div>`
return self._view.viewport
}
addTab (title, cssClass, content) {
const self = this
if (helper.checkSpecialChars(title)) return
if (self._view.contents[title] || self._view.tabs[title]) throw new Error('tab already exists')
self._view.contents[title] = content
self._view.tabs[title] = yo`<li class="${css.options} ${cssClass}" onclick=${function (ev) { self.selectTab(this) }} title=${title}>${title}</li>`
if (self._view.el) self._view.el.appendChild(self._view.tabs[title])
if (self._view.viewport) self._view.viewport.appendChild(self._view.contents[title])
}
removeTabByTitle (title) {
const self = this
if (self._view.tabs[title]) {
self._view.tabs[title].parentNode.removeChild(self._view.tabs[title])
}
if (self._view.contents[title]) {
self._view.contents[title].parentNode.removeChild(self._view.contents[title])
}
delete self._view.contents[title]
delete self._view.tabs[title]
}
getTabByClass (tabClass) {
const self = this
return self._view.el.querySelector(`li.${tabClass}`)
}
updateTabTitle (tabClass, title) {
const self = this
var tab = self.getTabByClass(tabClass)
if (tab) tab.innerHTML = title
}
selectTabByTitle (title) {
const self = this
self.selectTab(self._view.tabs[title])
}
selectTabByClassName (tabClass) {
const self = this
var tab = self.getTabByClass(tabClass)
if (tab) self.selectTab(tab)
return tab
}
selectTab (el) {
const self = this
if (!el.classList.contains(css.active)) {
var nodes = Object.values(self._view.tabs)
for (var i = 0; i < nodes.length; ++i) {
nodes[i].classList.remove(css.active)
self._view.contents[nodes[i].getAttribute('title')].style.display = 'none'
}
}
var title = el.getAttribute('title')
self._view.contents[el.getAttribute('title')].style.display = 'block'
el.classList.add(css.active)
self._deps.app.event.trigger('tabChanged', [title])
}
}
module.exports = TabbedMenu
This diff is collapsed.
var helper = require('../../../lib/helper.js')
var modalDialogCustom = require('../../ui/modal-dialog-custom')
class TestTabLogic {
constructor (fileManager) {
this.fileManager = fileManager
}
generateTestFile () {
var path = this.fileManager.currentPath()
var fileProvider = this.fileManager.fileProviderOf(path)
if (!fileProvider) return
helper.createNonClashingNameWithPrefix(path + '/test.sol', fileProvider, '_test', (error, newFile) => {
if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error)
if (!fileProvider.set(newFile, this.generateTestContractSample())) return modalDialogCustom.alert('Failed to create test file ' + newFile)
this.fileManager.switchFile(newFile)
})
}
async getTests (cb) {
var path = this.fileManager.currentPath()
if (!path) return cb(null, [])
var provider = this.fileManager.fileProviderOf(path)
if (!provider) return cb(null, [])
var tests = []
let files
try {
files = await this.fileManager.getFilesFromPath(path)
} catch (e) {
cb(e.message)
}
for (var file in files) {
if (/.(_test.sol)$/.exec(file)) tests.push(provider.type + '/' + file)
}
cb(null, tests)
}
generateTestContractSample () {
return `pragma solidity >=0.4.0 <0.6.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
// file name has to end with '_test.sol'
contract test_1 {
function beforeAll() public {
// here should instantiate tested contract
Assert.equal(uint(4), uint(3), "error in before all function");
}
function check1() public {
// use 'Assert' to test the contract
Assert.equal(uint(2), uint(1), "error message");
Assert.equal(uint(2), uint(2), "error message");
}
function check2() public view returns (bool) {
// use the return value (true or false) to test the contract
return true;
}
}
contract test_2 {
function beforeAll() public {
// here should instantiate tested contract
Assert.equal(uint(4), uint(3), "error in before all function");
}
function check1() public {
// use 'Assert' to test the contract
Assert.equal(uint(2), uint(1), "error message");
Assert.equal(uint(2), uint(2), "error message");
}
function check2() public view returns (bool) {
// use the return value (true or false) to test the contract
return true;
}
}`
}
}
module.exports = TestTabLogic
import { ApiFactory } from 'remix-plugin'
import { EventEmitter } from 'events'
export class TxListenerModule extends ApiFactory {
constructor (txlistener) {
super()
this.events = new EventEmitter()
txlistener.event.register('newTransaction', (tx) => {
this.events.emit('newTransaction', tx)
})
}
get profile () {
return {
name: 'txListener',
displayName: 'transaction listener',
events: ['newTransaction'],
description: 'service - notify new transactions'
}
}
}
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 modal = require('./modaldialog.js') var modal = require('./modaldialog.js')
// -------------- styling ---------------------- // -------------- styling ----------------------
var css = require('./styles/auto-complete-popup-styles') var css = require('./styles/auto-complete-popup-styles')
// var cssModal = require('./styles/modaldialog-styles')
/* USAGE: /* USAGE:
...@@ -22,118 +22,180 @@ class AutoCompletePopup { ...@@ -22,118 +22,180 @@ class AutoCompletePopup {
constructor (opts = {}) { constructor (opts = {}) {
var self = this var self = this
self.event = new EventManager() self.event = new EventManager()
self.isOpen = false
self.data = { self.data = {
_options: opts.options || [] _options: opts.options || []
} }
self._components = {
modal: null
}
self._view = {} self._view = {}
self._startingElement = 0 self._startingElement = 0
self._elementsToShow = 3 self._elementsToShow = 4
self._removePopUp = this.resetCSSValuesModalContainer self._selectedElement = 0
} this.render()
resetCSSValuesModalContainer () {
/*
var modalContainer = document.querySelector(`.${cssModal.modal}`)
modalContainer.style.display = 'none'
var modalContent = document.querySelector(`.${css.modalContent}`)
let newModalContent = modalContent ? document.querySelector(`.${css.modalContent}`) : document.querySelector(`.${cssModal.modalContent}`)
newModalContent.className = cssModal.modalContent
*/
} }
render () { render () {
var self = this var self = this
var header = yo`<div class="${css.text}">Remix Commands</div>`
self._view.autoComplete = yo` self._view.autoComplete = yo`
<div class="${css.popup}"> <div class="${css.popup}">
<div> <div>
${self.data._options.map((item, index) => { ${self.data._options.map((item, index) => {
return yo` return yo`
<div class="${css.listHandlerHide}"> <div class="${css.autoCompleteItem} ${css.listHandlerHide} item ${self._selectedElement === index ? 'bg-secondary' : ''}">
<a value=${index}> <div value=${index} onclick=${(event) => { self.handleSelect(event.srcElement.innerText) }}>
<div onclick=${handleSelect}> ${getKeyOf(item)}
${Object.keys(item)[0]}
</div> </div>
</a>
<div> <div>
${Object.values(item)[0]} ${getValueOf(item)}
</div> </div>
<hr/>
</div> </div>
` `
})} })}
</div> </div>
<div class="${css.listHandlerHide}"> <div class="${css.listHandlerHide}">
<button value=false onclick=${handleListIteration}>▲</button>
<button value=true onclick=${handleListIteration}>▼</button>
<div class="${css.pageNumberAlignment}">Page ${(self._startingElement / self._elementsToShow) + 1} of ${Math.ceil(self.data._options.length / self._elementsToShow)}</div> <div class="${css.pageNumberAlignment}">Page ${(self._startingElement / self._elementsToShow) + 1} of ${Math.ceil(self.data._options.length / self._elementsToShow)}</div>
</div> </div>
</div> </div>
` `
function setUpPopUp () { function setUpPopUp () {
handleOpenPopup() handleOpenPopup()
handleNagivationButtons()
handleListSize() handleListSize()
} }
function handleOpenPopup () { function handleOpenPopup () {
if (self.data._options.length > 1) { if (self.data._options.length > 0) {
self._view.autoComplete.style.display = 'block' self._view.autoComplete.style.display = 'block'
modal(header.innerText, self._view.autoComplete, {label: null}, self._components.modal = modal('', self._view.autoComplete, {label: null}, {label: null}, null, { class: css.modalContent, hideClose: true })
{
fn: () => { self._removePopUp() }
})
editCSSValuesModalContainer()
}
}
function handleSelect (event) {
self._removePopUp()
self._view.autoComplete.style.display = 'none'
self.event.trigger('handleSelect', [event.srcElement.innerText])
}
function handleNagivationButtons () {
if (self.data._options.length > self._elementsToShow) {
self._view.autoComplete.children[1].className = css.listHandlerButtonShow
} }
} }
function handleListSize () { function handleListSize () {
if (self.data._options.length >= self._startingElement) { if (self.data._options.length >= self._startingElement) {
for (let i = self._startingElement; i < (self._elementsToShow + self._startingElement); i++) { for (let i = self._startingElement; i < (self._elementsToShow + self._startingElement); i++) {
if (self._view.autoComplete.children[0].children[i]) { let el = self._view.autoComplete.querySelectorAll('.item')[i]
self._view.autoComplete.children[0].children[i].className = css.listHandlerShow if (el) {
el.classList.remove(css.listHandlerHide)
el.classList.add(css.listHandlerShow)
} }
} }
} }
} }
function handleListIteration (event) { setUpPopUp()
if (event.srcElement.value === 'true' || event.which === 40) {
if ((self._startingElement + self._elementsToShow) < self.data._options.length) { return self._view
self._startingElement += self._elementsToShow
} }
} else {
if (self._startingElement > 0) { handleSelect (text) {
self._startingElement -= self._elementsToShow this.removeAutoComplete()
this.event.trigger('handleSelect', [text])
}
moveUp () {
if (this._selectedElement === 0) return
this._selectedElement--
this._startingElement = this._selectedElement > 0 ? this._selectedElement - 1 : 0
this.event.trigger('updateList')
yo.update(this._view, this.render())
}
moveDown () {
if (this.data._options.length <= this._selectedElement + 1) return
this._selectedElement++
this._startingElement = this._selectedElement - 1
this.event.trigger('updateList')
yo.update(this._view, this.render())
}
handleAutoComplete (event, inputString) {
if (this.isOpen && (event.which === 27 || event.which === 8 || event.which === 46)) {
// backspace or any key that should remove the autocompletion
this.removeAutoComplete()
return true
}
if (this.isOpen && (event.which === 13 || event.which === 9)) {
// enter and tab (validate completion)
event.preventDefault()
if (this.data._options[this._selectedElement]) {
this.handleSelect(getKeyOf(this.data._options[this._selectedElement]))
}
this.removeAutoComplete()
return true
}
if (this.isOpen && event.which === 38) {
// move up
event.preventDefault()
this.isOpen = true
this.moveUp()
return true
}
if (this.isOpen && event.which === 40) {
// move down
event.preventDefault()
this.isOpen = true
this.moveDown()
return true
}
if (event.which === 13 || event.which === 9) {
// enter || tab and autocompletion is off, just returning false
return false
}
let textList = inputString.split(' ')
let autoCompleteInput = textList.length > 1 ? textList[textList.length - 1] : textList[0]
if (inputString.length >= 2) {
// more than 2 letters, start completion
this.isOpen = true
this.data._options = []
Commands.allPrograms.forEach(item => {
let program = getKeyOf(item)
if (program.substring(0, program.length - 1).includes(autoCompleteInput.trim())) {
this.data._options.push(item)
} else if (autoCompleteInput.trim().includes(program) || (program === autoCompleteInput.trim())) {
Commands.allCommands.forEach(item => {
let command = getKeyOf(item)
if (command.includes(autoCompleteInput.trim())) {
this.data._options.push(item)
} }
})
} }
self.event.trigger('updateList') })
if (this.data._options.length === 1 && event.which === 9) {
// if only one option and tab is pressed, we resolve it
event.preventDefault()
textList.pop()
textList.push(getKeyOf(this.data._options[0]))
this.handleSelect(`${textList}`.replace(/,/g, ' '))
this.removeAutoComplete()
return
} }
function editCSSValuesModalContainer () { yo.update(this._view, this.render())
/* return true
var modalContent = document.querySelector(`.${cssModal.modalContent}`) }
let newModalContent = modalContent ? document.querySelector(`.${cssModal.modalContent}`) : document.querySelector(`.${css.modalContent}`) return false
newModalContent.className = css.modalContent
*/
} }
setUpPopUp() removeAutoComplete () {
return self._view if (!this.isOpen) return
this._view.autoComplete.style.display = 'none'
if (this._components.modal) this._components.modal.cancelListener()
this.isOpen = false
this.data._options = []
this._startingElement = 0
this._selectedElement = 0
yo.update(this._view, this.render())
} }
}
function getKeyOf (item) {
return Object.keys(item)[0]
}
function getValueOf (item) {
return Object.values(item)[0]
} }
module.exports = AutoCompletePopup module.exports = AutoCompletePopup
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
import LandingPage from './landing-page' import LandingPage from './landing-page'
import Section from './section' import Section from './section'
import { defaultWorkspaces } from './workspace' import { defaultWorkspaces } from './workspace'
// var globalRegistry = require('../../../global/registry')
export function homepageProfile () { export function homepageProfile () {
return { return {
displayName: 'home', displayName: 'Home',
name: 'home', name: 'home',
methods: [], methods: [],
events: [], events: [],
...@@ -16,31 +17,32 @@ export function homepageProfile () { ...@@ -16,31 +17,32 @@ export function homepageProfile () {
} }
export function generateHomePage (appManager, appStore) { export function generateHomePage (appManager, appStore) {
/* /* var actions1 = [
var actions1 = [ { label: 'New file',
{label: 'new file', type: `callback`, payload: () => { alert(`-new file created-`) }}, type: 'callback',
{label: 'import from GitHub', type: `callback`, payload: () => { alert(`-imported from GitHub-`) }}, payload: () => {
{label: 'import from gist', type: `callback`, payload: () => { alert(`-imported from gist-`) }} let fileManager = globalRegistry.get('fileexplorerbrowser').api
] fileManager.creatNewFile()
}
var actions2 = [ },
{label: '...', type: `callback`, payload: () => { alert(`-...-`) }} {label: 'Import from GitHub', type: `callback`, payload: () => { this.alert(`-imported from GitHub-`) }},
] {label: 'Import from gist', type: `callback`, payload: () => { this.alert(`-imported from gist-`) }}
] */
var actions3 = [ var actions3 = [
{label: 'Remix documentation', type: `link`, payload: `https://remix.readthedocs.io/en/latest/#`}, {label: 'Remix documentation', type: `link`, payload: `https://remix.readthedocs.io/en/latest/#`},
{label: 'GitHub repository', type: `link`, payload: `https://github.com/ethereum/remix-ide`}, {label: 'GitHub repository', type: `link`, payload: `https://github.com/ethereum/remix-ide`},
{label: 'acces local file system (remixd)', type: `link`, payload: `https://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html`}, {label: 'Access local file system with remixd', type: `link`, payload: `https://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html`},
{label: 'npm module for remixd', type: `link`, payload: `https://www.npmjs.com/package/remixd`}, {label: 'npm module for remixd', type: `link`, payload: `https://www.npmjs.com/package/remixd`},
{label: 'medium posts', type: `link`, payload: `https://medium.com/remix-ide`}, {label: 'Medium posts', type: `link`, payload: `https://medium.com/remix-ide`},
{label: 'tutorials', type: `link`, payload: `https://github.com/ethereum/remix-workshops`} {label: 'Tutorials', type: `link`, payload: `https://github.com/ethereum/remix-workshops`}
] ]
var actions4 = [ var actions4 = [
{label: 'Remix plugins & modules', type: `link`, payload: `https://github.com/ethereum/remix-plugin/blob/master/readme.md`}, {label: 'Remix plugins & modules', type: `link`, payload: `https://github.com/ethereum/remix-plugin/blob/master/readme.md`},
{label: 'repository on GitHub', type: `link`, payload: `https://github.com/ethereum/remix-plugin`}, {label: 'Repository on GitHub', type: `link`, payload: `https://github.com/ethereum/remix-plugin`},
{label: 'examples', type: `link`, payload: `https://github.com/ethereum/remix-plugin/tree/master/examples`}, {label: 'Examples', type: `link`, payload: `https://github.com/ethereum/remix-plugin/tree/master/examples`},
{label: 'build plugin for Remix', type: `link`, payload: `https://medium.com/remix-ide/build-a-plugin-for-remix-90d43b209c5a`} {label: 'Build a plugin for Remix', type: `link`, payload: `https://medium.com/remix-ide/build-a-plugin-for-remix-90d43b209c5a`}
] ]
var actions5 = [ var actions5 = [
...@@ -49,12 +51,10 @@ export function generateHomePage (appManager, appStore) { ...@@ -49,12 +51,10 @@ export function generateHomePage (appManager, appStore) {
{label: 'Reddit', type: `link`, payload: `https://www.reddit.com/r/ethdev/search?q=remix&restrict_sr=1`} {label: 'Reddit', type: `link`, payload: `https://www.reddit.com/r/ethdev/search?q=remix&restrict_sr=1`}
] ]
var section1 = new Section('Start', actions1) // var sectionStart = new Section('Start', actions1)
var section2 = new Section('Recent', actions2) var sectionLearn = new Section('Learn', actions3)
var section3 = new Section('Learn', actions3) var sectionPlugins = new Section('Plugins', actions4)
var section4 = new Section('Plugins', actions4) var sectionHelp = new Section('Help', actions5)
var section5 = new Section('Help', actions5)
*/
var sectionsWorkspaces = [] var sectionsWorkspaces = []
sectionsWorkspaces.push({ sectionsWorkspaces.push({
...@@ -66,8 +66,13 @@ export function generateHomePage (appManager, appStore) { ...@@ -66,8 +66,13 @@ export function generateHomePage (appManager, appStore) {
.forEach(({profile}) => { appManager.deactivateOne(profile.name) }) .forEach(({profile}) => { appManager.deactivateOne(profile.name) })
}}) }})
defaultWorkspaces(appManager).forEach((workspace) => { defaultWorkspaces(appManager).forEach((workspace) => {
sectionsWorkspaces.push({label: workspace.title, type: 'callback', payload: () => { workspace.activate() }}) sectionsWorkspaces.push({
label: workspace.title,
type: 'callback',
payload: () => { workspace.activate() }
})
}) })
var sectionWorkspace = new Section('Workspaces', sectionsWorkspaces) var sectionWorkspace = new Section('Workspaces', sectionsWorkspaces)
return new LandingPage([sectionWorkspace])
return new LandingPage([sectionWorkspace, /* sectionStart, */sectionLearn, sectionPlugins, sectionHelp])
} }
This diff is collapsed.
var yo = require('yo-yo') var yo = require('yo-yo')
var css = require('./styles/modaldialog-styles') var css = require('./styles/modaldialog-styles')
module.exports = (title, content, ok, cancel, focusSelector) => { module.exports = (title, content, ok, cancel, focusSelector, opts) => {
opts = opts || {}
var container = document.querySelector(`.${css.modal}`) var container = document.querySelector(`.${css.modal}`)
if (!container) { if (!container) {
document.querySelector('body').appendChild(html()) document.querySelector('body').appendChild(html(opts))
container = document.querySelector(`.${css.modal}`) container = document.querySelector(`.${css.modal}`)
} }
var closeDiv = document.getElementById('modal-close') var closeDiv = document.getElementById('modal-close')
if (opts.hideClose) closeDiv.style.display = 'none'
var okDiv = document.getElementById('modal-footer-ok') var okDiv = document.getElementById('modal-footer-ok')
okDiv.innerHTML = (ok && ok.label !== undefined) ? ok.label : 'OK' okDiv.innerHTML = (ok && ok.label !== undefined) ? ok.label : 'OK'
okDiv.style.display = okDiv.innerHTML === '' ? 'none' : 'inline-block'
var cancelDiv = document.getElementById('modal-footer-cancel') var cancelDiv = document.getElementById('modal-footer-cancel')
cancelDiv.innerHTML = (cancel && cancel.label !== undefined) ? cancel.label : 'Cancel' cancelDiv.innerHTML = (cancel && cancel.label !== undefined) ? cancel.label : 'Cancel'
...@@ -38,6 +41,10 @@ module.exports = (title, content, ok, cancel, focusSelector) => { ...@@ -38,6 +41,10 @@ module.exports = (title, content, ok, cancel, focusSelector) => {
removeEventListener() removeEventListener()
hide() hide()
if (cancel && cancel.fn) cancel.fn() if (cancel && cancel.fn) cancel.fn()
if (container) {
container.class = css.modal
container = null
}
} }
function modalKeyEvent (e) { function modalKeyEvent (e) {
...@@ -50,10 +57,11 @@ module.exports = (title, content, ok, cancel, focusSelector) => { ...@@ -50,10 +57,11 @@ module.exports = (title, content, ok, cancel, focusSelector) => {
} }
function hide () { function hide () {
container.style.display = 'none' if (container) container.style.display = 'none'
} }
function show () { function show () {
if (!container) return
container.style.display = 'block' container.style.display = 'block'
if (focusSelector) { if (focusSelector) {
const focusTarget = document.querySelector(`.${css.modal} ${focusSelector}`) const focusTarget = document.querySelector(`.${css.modal} ${focusSelector}`)
...@@ -71,19 +79,24 @@ module.exports = (title, content, ok, cancel, focusSelector) => { ...@@ -71,19 +79,24 @@ module.exports = (title, content, ok, cancel, focusSelector) => {
cancelDiv.removeEventListener('click', cancelListener) cancelDiv.removeEventListener('click', cancelListener)
closeDiv.removeEventListener('click', cancelListener) closeDiv.removeEventListener('click', cancelListener)
document.removeEventListener('keydown', modalKeyEvent) document.removeEventListener('keydown', modalKeyEvent)
if (document.getElementById('modal-background')) {
document.getElementById('modal-background').removeEventListener('click', cancelListener) document.getElementById('modal-background').removeEventListener('click', cancelListener)
} }
}
okDiv.addEventListener('click', okListener) okDiv.addEventListener('click', okListener)
cancelDiv.addEventListener('click', cancelListener) cancelDiv.addEventListener('click', cancelListener)
closeDiv.addEventListener('click', cancelListener) closeDiv.addEventListener('click', cancelListener)
document.addEventListener('keydown', modalKeyEvent) document.addEventListener('keydown', modalKeyEvent)
if (document.getElementById('modal-background')) {
document.getElementById('modal-background').addEventListener('click', cancelListener) document.getElementById('modal-background').addEventListener('click', cancelListener)
}
return { container, okListener, cancelListener }
} }
function html () { function html (opts) {
return yo`<div id="modal-dialog" class="${css.modal}"> return yo`<div id="modal-dialog" class="${css.modal}">
<div id="modal-background" class="${css['modalBackground']}"> </div> <div id="modal-background" class="${css['modalBackground']}"> </div>
<div class="${css['modalContent']} bg-light text-secondary"> <div class="${css['modalContent']} bg-light text-secondary ${opts.class}">
<div class="${css['modalHeader']}"> <div class="${css['modalHeader']}">
<h3></h3> <h3></h3>
<i id="modal-close" title="Close" class="fa fa-times ${css['modalClose']}" aria-hidden="true"></i> <i id="modal-close" title="Close" class="fa fa-times ${css['modalClose']}" aria-hidden="true"></i>
......
...@@ -11,6 +11,11 @@ var css = csjs` ...@@ -11,6 +11,11 @@ var css = csjs`
padding-bottom : 13px; padding-bottom : 13px;
} }
.autoCompleteItem {
padding : 4px;
border-radius : 2px;
}
.popup a { .popup a {
cursor : pointer; cursor : pointer;
} }
......
export default { export default {
start: (appStore, swapPanelApi, verticalIconApi, mainPanelApi, resizeFeature) => { start: (appStore, swapPanelApi, verticalIconApi, mainPanelApi, resizeFeature) => {
swapPanelApi.event.on('toggle', (moduleName) => { swapPanelApi.event.on('toggle', () => {
resizeFeature.panel1.clientWidth !== 0 ? resizeFeature.minimize() : resizeFeature.maximise() resizeFeature.panel1.clientWidth !== 0 ? resizeFeature.minimize() : resizeFeature.maximise()
}) })
swapPanelApi.event.on('showing', (moduleName) => { swapPanelApi.event.on('showing', () => {
resizeFeature.panel1.clientWidth === 0 ? resizeFeature.maximise() : '' resizeFeature.panel1.clientWidth === 0 ? resizeFeature.maximise() : ''
var current = appStore.getOne(moduleName)
// warn the content that it is being displayed. TODO should probably be done in each view
if (current && current.api.__showing) current.api.__showing()
}) })
mainPanelApi.event.on('toggle', () => { mainPanelApi.event.on('toggle', () => {
resizeFeature.maximise() resizeFeature.maximise()
......
...@@ -135,7 +135,8 @@ export class EntityStore extends Store { ...@@ -135,7 +135,8 @@ export class EntityStore extends Store {
* Add a new entity to the state * Add a new entity to the state
* @param {Object} entity * @param {Object} entity
*/ */
add (id, entity) { add (entity) {
const id = entity[this.keyId]
this.state.entities[id] = entity this.state.entities[id] = entity
this.state.ids.push(id) this.state.ids.push(id)
this.event.emit('add', entity) this.event.emit('add', entity)
......
...@@ -14,8 +14,8 @@ export class RemixAppManager extends AppManagerApi { ...@@ -14,8 +14,8 @@ export class RemixAppManager extends AppManagerApi {
} }
} }
ensureActivated (module) { ensureActivated (apiName) {
if (!this.store.isActive(module)) this.activateOne(module) if (!this.store.isActive(apiName)) this.activateOne(apiName)
} }
proxy () { proxy () {
...@@ -24,33 +24,23 @@ export class RemixAppManager extends AppManagerApi { ...@@ -24,33 +24,23 @@ export class RemixAppManager extends AppManagerApi {
} }
setActive (name, isActive) { setActive (name, isActive) {
const entity = this.getEntity(name) const api = this.getEntity(name)
// temp // temp
if (entity && (name === 'solidity' || name === 'vyper')) { if (api && (name === 'solidity' || name === 'vyper')) {
isActive ? this.data.proxy.register(name, entity.api) : this.data.proxy.unregister(name, entity.api) isActive ? this.data.proxy.register(name, api) : this.data.proxy.unregister(name, api)
} }
isActive ? this.store.activate(name) : this.store.deactivate(name) isActive ? this.store.activate(name) : this.store.deactivate(name)
if (!isActive) { if (!isActive) {
this.removeHiddenServices(entity) this.removeHiddenServices(api)
} }
} }
getEntity (entityName) { getEntity (apiName) {
return this.store.getOne(entityName) return this.store.getOne(apiName)
} }
addEntity (entity) { addEntity (api) {
this.store.add(entity.profile.name, entity) this.store.add(api)
}
// this function is only used for iframe plugins
resolveLocation (profile, domEl) {
if (profile.icon) {
this.event.emit('pluginNeedsLocation', profile, domEl)
} else {
this.hiddenServices[profile.name] = domEl
document.body.appendChild(domEl)
}
} }
removeHiddenServices (profile) { removeHiddenServices (profile) {
...@@ -78,13 +68,14 @@ export class RemixAppManager extends AppManagerApi { ...@@ -78,13 +68,14 @@ export class RemixAppManager extends AppManagerApi {
notifications: { notifications: {
'solidity': ['compilationFinished'] 'solidity': ['compilationFinished']
}, },
url: 'https://remix-pipeline.surge.sh', url: 'https://pipeline-alpha.pipeos.one',
description: ' - ', description: 'Visual IDE for contracts and dapps',
icon: 'data:image/png;base64,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', icon: 'data:image/png;base64,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',
prefferedLocation: 'mainPanel' location: 'mainPanel'
} }
const plugins = [{ profile: pipeline, api: new Plugin(pipeline, { resolveLocaton: (iframe) => { return this.resolveLocation(pipeline, iframe) } }) }, return [
{ profile: vyper, api: new Plugin(vyper, { resolveLocaton: (iframe) => { return this.resolveLocation(vyper, iframe) } }) }] new Plugin(pipeline),
return plugins new Plugin(vyper)
]
} }
} }
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment