Unverified Commit 63f7e029 authored by yann300's avatar yann300 Committed by GitHub

Merge pull request #1656 from ethereum/swap_it

Swap it
parents d5e1fa2b 66d80cf4
......@@ -7,6 +7,7 @@
"transform-es2015-classes",
"transform-es2015-computed-properties",
"transform-es2015-destructuring",
"transform-object-rest-spread",
"transform-es2015-duplicate-keys",
"transform-es2015-for-of",
"transform-es2015-function-name",
......
......@@ -19,17 +19,17 @@ jobs:
- ENCRYPTION_LABEL3: "1b1c118ea62d"
- COMMIT_AUTHOR_EMAIL: "chris@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
- FILES_TO_PACKAGE: "assets background.js build icon.png index.html manifest.json README.md soljson.js"
- FILES_TO_PACKAGE: "assets background.js build icon.png index.html manifest.json README.md soljson.js package.json"
working_directory: ~/repo
steps:
- checkout
- restore_cache:
keys:
- dep-bundle-27-{{ checksum "package.json" }}
- dep-bundle-29-{{ checksum "package.json" }}
- run: npm install
- save_cache:
key: dep-bundle-27-{{ checksum "package.json" }}
key: dep-bundle-29-{{ checksum "package.json" }}
paths:
- ~/repo/node_modules
- run: npm run lint && npm run test && npm run make-mock-compiler && npm run build
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
#!/usr/bin/env node
var path = require('path')
var httpServer = require('http-server')
var remixd = require('remixd')
var server = httpServer.createServer({
root: __dirname + '/../'
root: path.join(__dirname, '/../')
})
var folder = process.argv.length > 2 ? process.argv[2] : process.cwd()
server.listen(8080, '127.0.0.1', function () {})
var router = new remixd.Router(65520, remixd.services.sharedFolder, { remixIdeUrl: 'http://localhost:8080' }, (webSocket) => {
remixd.services.sharedFolder.setWebSocket(webSocket)
remixd.services.sharedFolder.setupNotifications(folder)
remixd.services.sharedFolder.sharedFolder(folder, false)
})
remixd.services.sharedFolder.setWebSocket(webSocket)
remixd.services.sharedFolder.setupNotifications(folder)
remixd.services.sharedFolder.sharedFolder(folder, false)
})
router.start()
......
......@@ -7,7 +7,7 @@ SHA=`git rev-parse --short --verify HEAD`
git config user.name "$COMMIT_AUTHOR"
git config user.email "$COMMIT_AUTHOR_EMAIL"
git checkout --orphan gh-pages
git rm --cached -r .
git rm --cached -r -f .
echo "# Automatic build" > README.md
echo "Built website from \`$SHA\`. See https://github.com/ethereum/remix-ide/ for details." >> README.md
echo "To use an offline copy, download \`remix-$SHA.zip\`." >> README.md
......
......@@ -7,7 +7,7 @@ SHA=`git rev-parse --short --verify HEAD`
git config user.name "$COMMIT_AUTHOR"
git config user.email "$COMMIT_AUTHOR_EMAIL"
git checkout --orphan gh-pages
git rm --cached -r .
git rm --cached -r -f .
echo "# Automatic build" > README.md
echo "Built website from \`$SHA\`. See https://github.com/ethereum/remix-ide/ for details." >> README.md
echo "To use an offline copy, download \`remix-$SHA.zip\`." >> README.md
......
......@@ -40,7 +40,7 @@ function load () {
}
})
setInterval(function () {
remix.call('app', 'detectNetWork', [], function (error, result) {
remix.call('network', 'detectNetWork', [], function (error, result) {
if (error) console.log(error)
if (network.innerHTML !== result[0].name + ' - ' + result[0].id) {
currentNetWork = result[0].name
......
......@@ -28,8 +28,9 @@
-->
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>Remix - Solidity IDE</title>
<link rel="stylesheet" id="theme-link"/>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
<link rel="stylesheet" href="assets/css/pygment_trac.css">
<link rel="stylesheet" href="assets/css/font-awesome.min.css">
<link rel="icon" type="x-icon" href="icon.png">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
</head>
......
{
"name": "remix-ide",
"version": "v0.7.5",
"version": "v0.8.0-alpha+002",
"description": "Minimalistic browser-based Solidity IDE",
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.8.1",
"@resolver-engine/imports": "^0.3.0",
"ace-mode-solidity": "^0.1.0",
"async": "^2.1.2",
"babel-eslint": "^7.1.1",
"babel-plugin-transform-modern-regexp": "0.0.6",
"babel-plugin-transform-object-assign": "^6.22.0",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-plugin-yo-yoify": "^0.3.3",
"babel-polyfill": "^6.22.0",
"babel-preset-env": "^1.6.1",
......@@ -21,7 +25,8 @@
"csjs-inject": "^1.0.1",
"csslint": "^1.0.2",
"deep-equal": "^1.0.1",
"ethereumjs-util": "^6.1.0",
"ethereumjs-util": "^5.1.2",
"events": "^3.0.0",
"execr": "^1.0.1",
"exorcist": "^0.4.0",
"fast-async": "6.3.1",
......@@ -38,11 +43,12 @@
"npm-link-local": "^1.1.0",
"npm-run-all": "^4.0.2",
"onchange": "^3.2.1",
"remix-debug": "0.3.1",
"remix-analyzer": "0.3.1",
"remix-lib": "0.4.1",
"remix-solidity": "0.3.1",
"remix-tests": "0.1.1",
"remix-analyzer": "0.3.5",
"remix-debug": "0.3.5",
"remix-lib": "0.4.5",
"remix-solidity": "0.3.5",
"remix-tabs": "^1.0.0",
"remix-tests": "0.1.6",
"remixd": "0.1.8-alpha.6",
"request": "^2.83.0",
"rimraf": "^2.6.1",
......@@ -60,7 +66,8 @@
"yo-yoify": "^3.7.3"
},
"dependencies": {
"http-server": "0.9.0",
"http-server-legacy": "latest",
"remix-plugin": "0.0.2-alpha.6",
"remixd": "0.1.8-alpha.6"
},
"repository": {
......@@ -105,7 +112,8 @@
"transform-es2015-spread",
"transform-es2015-parameters",
"transform-es2015-destructuring",
"transform-es2015-block-scoping"
"transform-es2015-block-scoping",
"transform-modern-regexp"
]
},
"browserify": {
......@@ -146,7 +154,7 @@
"remix-ide": "./bin/remix-ide"
},
"scripts": {
"setupremix": "npm run linkremixlib && npm run linkremixsolidity && npm run linkremixsimulator && npm run linkremixtests",
"setupremix": "npm run linkremixdebug && npm run linkremixlib && npm run linkremixsolidity && npm run linkremixsimulator && npm run linkremixtests",
"pullremix": "git clone https://github.com/ethereum/remix",
"linkremixlib": "cd node_modules && rm -rf remix-lib && ln -s ../../remix/remix-lib remix-lib && cd ..",
"linkremixsolidity": "cd node_modules && rm -rf remix-solidity && ln -s ../../remix/remix-solidity remix-solidity && cd ..",
......@@ -171,15 +179,16 @@
"nightwatch_remote_debugger_parallel": "nightwatch --config nightwatch_debugger.js --env safari,chrome,default",
"onchange": "onchange build/app.js -- npm-run-all lint",
"prepublish": "mkdirp build; npm-run-all -ls downloadsolc_root build",
"remixd": "./node_modules/remixd/bin/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-install": "selenium-standalone install",
"serve": "execr --silent http-server .",
"serve_debugger": "execr --silent http-server src/app/debugger/remix-debugger",
"serve": "npx http-server-legacy .",
"serve_debugger": "npx http-server-legacy src/app/debugger/remix-debugger",
"sourcemap": "exorcist --root ../ build/app.js.map > build/app.js",
"start": "npm-run-all -lpr serve watch onchange remixd",
"test": "npm run csslint; standard && node test/index.js",
"test": "csslint && standard && node test/index.js",
"test-browser": "npm-run-all -lpr selenium downloadsolc_root make-mock-compiler serve browsertest",
"watch": "watchify src/index.js -dv -p browserify-reload -o build/app.js --exclude solc"
"watch": "watchify src/index.js -dv -p browserify-reload -o build/app.js --exclude solc",
"reinstall": "rm ./node-modules/ -rf; rm package-lock.json; rm ./build/ -rf; npm install; npm run build"
}
}
This diff is collapsed.
'use strict'
var base64 = require('js-base64').Base64
var swarmgw = require('swarmgw')()
var resolver = require('@resolver-engine/imports').ImportsEngine()
var request = require('request')
module.exports = class CompilerImports {
......@@ -10,10 +11,20 @@ module.exports = class CompilerImports {
}
handleGithubCall (root, path, cb) {
var accessToken = this.githubAccessToken() ? '?access_token=' + this.githubAccessToken() : ''
let param = '?'
param += this.githubAccessToken() ? 'access_token=' + this.githubAccessToken() : ''
const regex = path.match(/blob\/([^/]+)\/(.*)/)
if (regex) {
// if we have /blob/master/+path we extract the branch name "master" and add it as a parameter to the github api
// the ref can be branch name, tag, commit id
const reference = regex[1]
param += 'ref=' + reference
path = path.replace(`blob/${reference}/`, '')
}
return request.get(
{
url: 'https://api.github.com/repos/' + root + '/contents/' + path + accessToken,
url: 'https://api.github.com/repos/' + root + '/contents/' + path + param,
json: true
},
(err, r, data) => {
......@@ -112,13 +123,22 @@ module.exports = class CompilerImports {
})
}
})
if (found) return
if (found) {
return
} else if (/^[^:]*:\/\//.exec(url)) {
cb('Unable to import "' + url + '": Unsupported URL schema')
} else {
resolver
.resolve(url)
.then(result => {
return resolver.require(url)
})
.then(result => {
if (url.indexOf(result.provider + ':') === 0) {
url = url.substring(result.provider.length + 1) // remove the github prefix
}
cb(null, result.source, url, result.provider, result.url)
})
.catch(err => {
err
cb('Unable to import "' + url + '": File not found')
}
})
}
}
/* global localStorage */
const yo = require('yo-yo')
const modalDialog = require('../ui/modaldialog')
const unexposedEvents = ['statusChanged']
module.exports = class LocalPlugin {
/**
* Open a modal to create a local plugin
* @param {PluginApi[]} plugins The list of the plugins in the store
* @returns {Promise<{api: any, profile: any}>} A promise with the new plugin profile
*/
open (plugins) {
this.profile = JSON.parse(localStorage.getItem('plugins/local')) || { notifications: {} }
return new Promise((resolve, reject) => {
const onValidation = () => {
try {
const profile = this.create()
resolve(profile)
} catch (err) {
reject(err)
}
}
modalDialog('Local Plugin', this.form(plugins),
{ fn: () => onValidation() },
{ fn: () => resolve() }
)
})
}
/**
* Create the object to add to the plugin-list
*/
create () {
const profile = {
...this.profile,
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xMjYyIDEwNzVxLTM3IDEyMS0xMzggMTk1dC0yMjggNzQtMjI4LTc0LTEzOC0xOTVxLTgtMjUgNC00OC41dDM4LTMxLjVxMjUtOCA0OC41IDR0MzEuNSAzOHEyNSA4MCA5Mi41IDEyOS41dDE1MS41IDQ5LjUgMTUxLjUtNDkuNSA5Mi41LTEyOS41cTgtMjYgMzItMzh0NDktNCAzNyAzMS41IDQgNDguNXptLTQ5NC00MzVxMCA1My0zNy41IDkwLjV0LTkwLjUgMzcuNS05MC41LTM3LjUtMzcuNS05MC41IDM3LjUtOTAuNSA5MC41LTM3LjUgOTAuNSAzNy41IDM3LjUgOTAuNXptNTEyIDBxMCA1My0zNy41IDkwLjV0LTkwLjUgMzcuNS05MC41LTM3LjUtMzcuNS05MC41IDM3LjUtOTAuNSA5MC41LTM3LjUgOTAuNSAzNy41IDM3LjUgOTAuNXptMjU2IDI1NnEwLTEzMC01MS0yNDguNXQtMTM2LjUtMjA0LTIwNC0xMzYuNS0yNDguNS01MS0yNDguNSA1MS0yMDQgMTM2LjUtMTM2LjUgMjA0LTUxIDI0OC41IDUxIDI0OC41IDEzNi41IDIwNCAyMDQgMTM2LjUgMjQ4LjUgNTEgMjQ4LjUtNTEgMjA0LTEzNi41IDEzNi41LTIwNCA1MS0yNDguNXptMTI4IDBxMCAyMDktMTAzIDM4NS41dC0yNzkuNSAyNzkuNS0zODUuNSAxMDMtMzg1LjUtMTAzLTI3OS41LTI3OS41LTEwMy0zODUuNSAxMDMtMzg1LjUgMjc5LjUtMjc5LjUgMzg1LjUtMTAzIDM4NS41IDEwMyAyNzkuNSAyNzkuNSAxMDMgMzg1LjV6Ii8+PC9zdmc+',
methods: [],
hash: `local-${this.profile.name}`,
location: 'swapPanel'
}
profile.events = profile.events.filter((item) => { return item !== '' })
if (!profile.name) throw new Error('Plugin should have a name')
if (!profile.url) throw new Error('Plugin should have an URL')
localStorage.setItem('plugins/local', JSON.stringify(profile))
return profile
}
/**
* Add or remove a notification to/from the profile
* @param {Event} e The event when checkbox changes
* @param {string} pluginName The name of the plugin
* @param {string} eventName The name of the event to listen on
*/
toggleNotification (e, pluginName, eventName) {
const {checked} = e.target
if (checked) {
if (!this.profile.notifications[pluginName]) this.profile.notifications[pluginName] = []
this.profile.notifications[pluginName].push(eventName)
} else {
this.profile.notifications[pluginName].splice(this.profile.notifications[pluginName].indexOf(eventName), 1)
if (this.profile.notifications[pluginName].length === 0) delete this.profile.notifications[pluginName]
}
}
updateName ({target}) {
this.profile.name = target.value
}
updateUrl ({target}) {
this.profile.url = target.value
}
updateDisplayName ({target}) {
this.profile.displayName = target.value
}
updateEvents ({target}, index) {
if (this.profile.events[index] !== undefined) {
this.profile.events[index] = target.value
}
}
/**
* The checkbox for a couple module / event
* @param {string} plugin The name of the plugin
* @param {string} event The name of the event exposed by the plugin
*/
notificationCheckbox (plugin, event) {
const notifications = this.profile.notifications || {}
const checkbox = notifications[plugin] && notifications[plugin].includes(event)
? yo`<input type="checkbox" checked onchange="${e => this.toggleNotification(e, plugin, event)}">`
: yo`<input type="checkbox" onchange="${e => this.toggleNotification(e, plugin, event)}">`
return yo`<div>
${checkbox}
<label>${plugin} - ${event}</label>
</div>`
}
/**
* The form to create a local plugin
* @param {ProfileApi[]} plugins Liste of profile of the plugins
*/
form (plugins = []) {
const name = this.profile.name || ''
const url = this.profile.url || ''
const displayName = this.profile.displayName || ''
const profiles = plugins
.filter(({profile}) => profile.events && profile.events.length > 0)
.map(({profile}) => profile)
const eventsForm = (events) => {
return yo`<div>${events.map((event, i) => {
return yo`<input class="form-control" onchange="${e => this.updateEvents(e, i)}" value="${event}" />`
})}</div>`
}
const eventsEl = eventsForm(this.profile.events || [])
const pushEvent = () => {
if (!this.profile.events) this.profile.events = []
this.profile.events.push('')
yo.update(eventsEl, eventsForm(this.profile.events))
}
const addEvent = yo`<button type="button" class="btn btn-sm btn-light" onclick="${() => pushEvent()}">Add an event</button>`
return yo`
<form id="local-plugin-form">
<div class="form-group">
<label for="plugin-name">Plugin Name <small>(required)</small></label>
<input class="form-control" onchange="${e => this.updateName(e)}" value="${name}" id="plugin-name" placeholder="Should be camelCase">
</div>
<div class="form-group">
<label for="plugin-displayname">Display Name</label>
<input class="form-control" onchange="${e => this.updateDisplayName(e)}" value="${displayName}" id="plugin-displayname" placeholder="Name in the header">
</div>
<div class="form-group">
<label for="plugin-url">Url <small>(required)</small></label>
<input class="form-control" onchange="${e => this.updateUrl(e)}" value="${url}" id="plugin-url" placeholder="ex: https://localhost:8000">
</div>
<div class="form-group">
<label>Events</label>
${eventsEl}${addEvent}
</div>
<div class="form-group">
<label>Notifications</label>
${profiles.map(({name, events}) => {
return events
.filter(event => !unexposedEvents.includes(event))
.map(event => this.notificationCheckbox(name, event))
})}
</div>
</form>`
}
}
const yo = require('yo-yo')
const csjs = require('csjs-inject')
const EventEmitter = require('events')
const LocalPlugin = require('./local-plugin')
import { Plugin, BaseApi } from 'remix-plugin'
const css = csjs`
.pluginSearch {
display: flex;
flex-direction: column;
align-items: center;
background-color: var(--light);
padding: 10px;
position: sticky;
top: 0;
z-index: 2;
margin-bottom: 0px;
}
.localPluginBtn {
margin-top: 15px;
}
.displayName {
text-transform: capitalize;
}
.description {
text-transform: capitalize;
}
.row {
display: flex;
flex-direction: row;
}
.isStuck {
background-color: var(--primary);
color:
}
`
const profile = {
name: 'pluginManager',
displayName: 'Plugin manager',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNzU1IDQ1M3EzNyAzOCAzNyA5MC41dC0zNyA5MC41bC00MDEgNDAwIDE1MCAxNTAtMTYwIDE2MHEtMTYzIDE2My0zODkuNSAxODYuNXQtNDExLjUtMTAwLjVsLTM2MiAzNjJoLTE4MXYtMTgxbDM2Mi0zNjJxLTEyNC0xODUtMTAwLjUtNDExLjV0MTg2LjUtMzg5LjVsMTYwLTE2MCAxNTAgMTUwIDQwMC00MDFxMzgtMzcgOTEtMzd0OTAgMzcgMzcgOTAuNS0zNyA5MC41bC00MDAgNDAxIDIzNCAyMzQgNDAxLTQwMHEzOC0zNyA5MS0zN3Q5MCAzN3oiLz48L3N2Zz4=',
description: 'Start/stop services, modules and plugins',
kind: 'settings',
location: 'swapPanel'
}
class PluginManagerComponent extends BaseApi {
constructor () {
super(profile)
this.event = new EventEmitter()
this.views = {
root: null,
items: {}
}
this.localPlugin = new LocalPlugin()
this.filter = ''
}
setApp (appManager) {
this.appManager = appManager
}
setStore (store) {
this.store = store
this.store.event.on('activate', (name) => { this.reRender() })
this.store.event.on('deactivate', (name) => { this.reRender() })
this.store.event.on('add', (api) => { this.reRender() })
this.store.event.on('remove', (api) => { this.reRender() })
}
renderItem (name) {
const api = this.store.getOne(name)
if (!api) return
const isActive = this.store.actives.includes(name)
const displayName = (api.profile.displayName) ? api.profile.displayName : name
const activationButton = isActive
? yo`
<button onclick="${_ => this.appManager.deactivateOne(name)}" class="btn btn-secondary btn-sm">
Deactivate
</button>`
: yo`
<button onclick="${_ => this.appManager.activateOne(name)}" class="btn btn-success btn-sm">
Activate
</button>`
return yo`
<article class="list-group-item py-1" title="${name}" >
<div class="${css.row} justify-content-between align-items-center">
<h6 class="${css.displayName}">${displayName}</h6>
${activationButton}
</div>
<p class="${css.description}">${api.profile.description}</p>
</article>
`
}
/***************
* SUB-COMPONENT
*/
/**
* Add a local plugin to the list of plugins
*/
async openLocalPlugin () {
try {
const profile = await this.localPlugin.open(this.store.getAll())
if (!profile) return
this.appManager.registerOne(new Plugin(profile))
this.appManager.activateOne(profile.name)
} catch (err) {
// TODO : Use an alert to handle this error instead of a console.log
console.log(`Cannot create Plugin : ${err.message}`)
}
}
render () {
// Filtering helpers
const isFiltered = (api) => api.name.toLowerCase().includes(this.filter)
const isNotRequired = ({profile}) => !profile.required
const sortByName = (a, b) => {
const nameA = a.name.toUpperCase()
const nameB = b.name.toUpperCase()
return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0
}
// Filter all active and inactive modules that are not required
const { actives, inactives } = this.store.getAll()
.filter(isFiltered)
.filter(isNotRequired)
.sort(sortByName)
.reduce(({actives, inactives}, api) => {
return this.store.actives.includes(api.name)
? { actives: [...actives, api.name], inactives }
: { inactives: [...inactives, api.name], actives }
}, { actives: [], inactives: [] })
const activeTile = actives.length !== 0
? yo`
<nav class="navbar navbar-expand-lg navbar-light bg-light justify-content-between align-items-center">
<span class="navbar-brand">Active Modules</span>
<span class="badge badge-pill badge-primary">${actives.length}</span>
</nav>`
: ''
const inactiveTile = inactives.length !== 0
? yo`
<nav class="navbar navbar-expand-lg navbar-light bg-light justify-content-between align-items-center">
<span class="navbar-brand">Inactive Modules</span>
<span class="badge badge-pill badge-primary">${inactives.length}</span>
</nav>`
: ''
const rootView = yo`
<div id='pluginManager'>
<div class="form-group ${css.pluginSearch}">
<input onkeyup="${e => this.filterPlugins(e)}" class="form-control" placeholder="Search">
<button onclick="${_ => this.openLocalPlugin()}" class="btn btn-sm text-info ${css.localPluginBtn}">
Connect to a Local Plugin
</button>
</div>
<section>
${activeTile}
<div class="list-group list-group-flush">
${actives.map(name => this.renderItem(name))}
</div>
${inactiveTile}
<div class="list-group list-group-flush">
${inactives.map(name => this.renderItem(name))}
</div>
</section>
</div>
`
if (!this.views.root) this.views.root = rootView
return rootView
}
reRender () {
if (this.views.root) {
yo.update(this.views.root, this.render())
}
}
filterPlugins ({ target }) {
this.filter = target.value.toLowerCase()
this.reRender()
}
}
module.exports = PluginManagerComponent
var registry = require('../../global/registry')
const CompilerAbstract = require('../compiler/compiler-abstract')
const EventManager = require('remix-lib').EventManager
class PluginManagerProxy {
constructor () {
this.event = new EventManager()
this._listeners = {}
this._listeners['vyper'] = (file, source, languageVersion, data) => {
registry.get('compilersartefacts').api['__last'] = new CompilerAbstract(languageVersion, data, source)
this.event.trigger('sendCompilationResult', [file, source, languageVersion, data])
}
this._listeners['solidity'] = (file, source, languageVersion, data) => {
registry.get('compilersartefacts').api['__last'] = new CompilerAbstract(languageVersion, data, source)
this.event.trigger('sendCompilationResult', [file, source, languageVersion, data])
}
}
register (name, instance) {
if (this._listeners[name]) {
instance.events.on('compilationFinished', this._listeners[name])
}
}
unregister (name, instance) {
if (this._listeners[name]) {
instance.events.removeListener('compilationFinished', this._listeners[name])
}
}
}
module.exports = PluginManagerProxy
import EventEmmitter from 'events'
class SwapPanelApi {
constructor (swapPanelComponent, verticalIconsComponent) {
this.event = new EventEmmitter()
this.component = swapPanelComponent
this.currentContent
verticalIconsComponent.events.on('toggleContent', (moduleName) => {
if (!swapPanelComponent.contents[moduleName]) return
if (this.currentContent === moduleName) {
this.event.emit('toggle', moduleName)
return
}
this.showContent(moduleName)
this.event.emit('showing', moduleName)
})
verticalIconsComponent.events.on('showContent', (moduleName) => {
if (!swapPanelComponent.contents[moduleName]) return
this.showContent(moduleName)
this.event.emit('showing', moduleName)
})
}
showContent (moduleName) {
this.component.showContent(moduleName)
this.currentContent = moduleName
}
/*
content: DOM element
by appManager
*/
add (profile, content) {
return this.component.add(profile.name, content)
}
remove (profile) {
return this.component.remove(profile.name)
}
}
module.exports = SwapPanelApi
var yo = require('yo-yo')
var csjs = require('csjs-inject')
class SwapPanelComponent {
constructor (name, appStore, appManager, opt) {
this.name = name
this.opt = opt
this.store = appStore
// list of contents
this.contents = {}
// name of the current displayed content
this.currentNode
this.store.event.on('activate', (name) => {
const api = this.store.getOne(name)
const profile = api.profile
if (((profile.location === this.name) || (!profile.location && opt.default)) &&
profile.icon && api.render && typeof api.render === 'function') {
this.add(name, api.render())
}
})
this.store.event.on('deactivate', (name) => {
if (this.contents[name]) this.remove(name)
})
this.store.event.on('add', (api) => { })
this.store.event.on('remove', (api) => { })
}
showContent (moduleName) {
// hiding the current view and display the `moduleName`
if (this.contents[moduleName]) {
if (this.currentNode) {
this.contents[this.currentNode].style.display = 'none'
}
this.contents[moduleName].style.display = 'block'
this.currentNode = moduleName
var api = this.store.getOne(moduleName)
this.header.querySelector('h6').innerHTML = api.profile ? api.profile.displayName : ' - '
return
}
}
add (moduleName, content) {
content.style.height = '100%'
content.style.width = '100%'
content.style.border = '0'
this.contents[moduleName] = yo`<div class=${css.plugItIn} >${content}</div>`
this.view.appendChild(this.contents[moduleName])
}
remove (moduleName) {
let el = this.contents[moduleName]
if (el) el.parentElement.removeChild(el)
}
render () {
this.view = yo`
<div id='plugins' class=${css.plugins}>
</div>
`
this.header = yo`<header class="${css.swapitHeader}"><h6 class="${css.swapitTitle}"></h6></header>`
if (!this.opt.displayHeader) this.header.style.display = 'none'
return yo`<div class=${css.pluginsContainer}>
${this.header}
${this.view}
</div>`
}
}
module.exports = SwapPanelComponent
const css = csjs`
.plugins {
height : 95%;
}
.plugItIn {
display : none;
height : 100%;
}
.plugItIn > div {
overflow-y : auto;
height : 100%;
width : 100%;
}
.plugItIn.active {
display : block;
}
.pluginsContainer {
height: 100%;
overflow-y: hidden;
}
.swapitTitle {
text-transform: uppercase;
}
.swapitHeader {
height: 35px;
padding-top: 10px;
padding-left: 27px;
}
`
// API
class VerticalIconsApi {
constructor (verticalIconsComponent) {
this.component = verticalIconsComponent
}
addIcon (mod) {
this.component.addIcon(mod)
}
removeIcon (mod) {
this.component.removeIcon(mod)
}
select (moduleName) {
this.component.select(moduleName)
}
}
module.exports = VerticalIconsApi
This diff is collapsed.
......@@ -12,8 +12,6 @@ var executionContext = require('../../execution-context')
var globalRegistry = require('../../global/registry')
var remixLib = require('remix-lib')
var Web3Providers = remixLib.vm.Web3Providers
var DummyProvider = remixLib.vm.DummyProvider
var init = remixLib.init
......@@ -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 {
constructor (container) {
this.registry = globalRegistry
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.sourceHighlighter = new SourceHighlighter()
......@@ -173,19 +111,29 @@ class DebuggerUI {
if (compilers['__last']) lastCompilationResult = compilers['__last']
// TODO debugging with source highlight is disabled. see line 98
this.debugger = new Debugger({
web3: this.contextManager.getWeb3(),
offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api,
compiler: { lastCompilationResult }
})
this.listenToEvents()
this.debugger.debugger.updateWeb3(this.executionContext.web3())
this.debugger.debug(blockNumber, txNumber, tx, () => {
self.stepManager = new StepManagerUI(this.debugger.step_manager)
self.vmDebugger = new VmDebugger(this.debugger.vmDebuggerLogic)
self.renderDebugger()
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({
web3,
offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api,
compiler: { lastCompilationResult }
})
this.listenToEvents()
this.debugger.debug(blockNumber, txNumber, tx, () => {
self.stepManager = new StepManagerUI(this.debugger.step_manager)
self.vmDebugger = new VmDebugger(this.debugger.vmDebuggerLogic)
self.renderDebugger()
})
})
}
......
......@@ -3,8 +3,6 @@ var EventManager = require('../../../lib/events')
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.buttons {
......@@ -17,7 +15,6 @@ var css = csjs`
justify-content: center;
}
.stepButton {
${styles.rightPanel.debuggerTab.button_Debugger}
}
.jumpButtons {
width: 100%;
......@@ -25,13 +22,10 @@ var css = csjs`
justify-content: center;
}
.jumpButton {
${styles.rightPanel.debuggerTab.button_Debugger}
}
.navigator {
color: ${styles.rightPanel.debuggerTab.text_Primary};
}
.navigator:hover {
color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor};
}
`
......@@ -51,20 +45,20 @@ function ButtonNavigator () {
ButtonNavigator.prototype.render = function () {
var self = this
var view = yo`<div class="${css.buttons}">
<div class="${css.stepButtons}">
<button id='overback' title='Step over back' class='${css.navigator} ${css.stepButton} fa fa-reply' onclick=${function () { self.event.trigger('stepOverBack') }} disabled=${this.overBackDisabled} ></button>
<button id='intoback' title='Step back' class='${css.navigator} ${css.stepButton} fa fa-level-up' onclick=${function () { self.event.trigger('stepIntoBack') }} disabled=${this.intoBackDisabled} ></button>
<button id='intoforward' title='Step into' class='${css.navigator} ${css.stepButton} fa fa-level-down' onclick=${function () { self.event.trigger('stepIntoForward') }} disabled=${this.intoForwardDisabled} ></button>
<button id='overforward' title='Step over forward' class='${css.navigator} ${css.stepButton} fa fa-share' onclick=${function () { self.event.trigger('stepOverForward') }} disabled=${this.overForwardDisabled} ></button>
<div class="${css.stepButtons} btn-group p-1">
<button id='overback' class='btn btn-primary btn-sm' title='Step over back' class='${css.navigator} ${css.stepButton} fas fa-reply' onclick=${function () { self.event.trigger('stepOverBack') }} disabled=${this.overBackDisabled} ></button>
<button id='intoback' class='btn btn-primary btn-sm' title='Step back' class='${css.navigator} ${css.stepButton} fas fa-level-up-alt' onclick=${function () { self.event.trigger('stepIntoBack') }} disabled=${this.intoBackDisabled} ></button>
<button id='intoforward' class='btn btn-primary btn-sm' title='Step into' class='${css.navigator} ${css.stepButton} fas fa-level-down-alt' onclick=${function () { self.event.trigger('stepIntoForward') }} disabled=${this.intoForwardDisabled} ></button>
<button id='overforward' class='btn btn-primary btn-sm' title='Step over forward' class='${css.navigator} ${css.stepButton} fas fa-share' onclick=${function () { self.event.trigger('stepOverForward') }} disabled=${this.overForwardDisabled} ></button>
</div>
<div class="${css.jumpButtons}">
<button id='jumppreviousbreakpoint' title='Jump to the previous breakpoint' class='${css.navigator} ${css.jumpButton} fa fa-step-backward' onclick=${function () { self.event.trigger('jumpPreviousBreakpoint') }} disabled=${this.jumpPreviousBreakpointDisabled} ></button>
<button id='jumpout' title='Jump out' class='${css.navigator} ${css.jumpButton} fa fa-eject' onclick=${function () { self.event.trigger('jumpOut') }} disabled=${this.jumpOutDisabled} ></button>
<button id='jumpnextbreakpoint' title='Jump to the next breakpoint' class='${css.navigator} ${css.jumpButton} fa fa-step-forward' onclick=${function () { self.event.trigger('jumpNextBreakpoint') }} disabled=${this.jumpNextBreakpointDisabled} ></button>
<div class="${css.jumpButtons} btn-group p-1">
<button class='btn btn-primary btn-sm' id='jumppreviousbreakpoint' title='Jump to the previous breakpoint' class='${css.navigator} ${css.jumpButton} fas fa-step-backward' onclick=${function () { self.event.trigger('jumpPreviousBreakpoint') }} disabled=${this.jumpPreviousBreakpointDisabled} ></button>
<button class='btn btn-primary btn-sm' id='jumpout' title='Jump out' class='${css.navigator} ${css.jumpButton} fas fa-eject' onclick=${function () { self.event.trigger('jumpOut') }} disabled=${this.jumpOutDisabled} ></button>
<button class='btn btn-primary btn-sm' id='jumpnextbreakpoint' title='Jump to the next breakpoint' class='${css.navigator} ${css.jumpButton} fas fa-step-forward' onclick=${function () { self.event.trigger('jumpNextBreakpoint') }} disabled=${this.jumpNextBreakpointDisabled} ></button>
</div>
<div id='reverted' style="display:none">
<button id='jumptoexception' title='Jump to exception' class='${css.navigator} ${css.button} fa fa-exclamation-triangle' onclick=${function () { self.event.trigger('jumpToException') }} disabled=${this.jumpOutDisabled} >
<button class='btn btn-danger btn-sm' id='jumptoexception' title='Jump to exception' class='${css.navigator} ${css.button} fas fa-exclamation-triangle' onclick=${function () { self.event.trigger('jumpToException') }} disabled=${this.jumpOutDisabled} >
</button>
<span>State changes made during this call will be reverted.</span>
<span id='outofgas' style="display:none">This call will run out of gas.</span>
......
var EventManager = require('../../../lib/events')
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.container {
......@@ -19,7 +17,6 @@ var css = csjs`
justify-content: center;
}
.txinput {
${styles.rightPanel.debuggerTab.input_Debugger}
margin: 3px;
width: inherit;
}
......@@ -29,14 +26,11 @@ var css = csjs`
justify-content: center;
}
.txbutton {
${styles.rightPanel.debuggerTab.button_Debugger}
width: inherit;
}
.txbuttonstart {
${styles.rightPanel.debuggerTab.button_Debugger}
}
.txbutton:hover {
color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor};
}
.vmargin {
margin-top: 10px;
......@@ -99,13 +93,13 @@ TxBrowser.prototype.render = function () {
var self = this
var view = yo`<div class="${css.container}">
<div class="${css.txContainer}">
<div class="${css.txinputs}">
<input class="${css.txinput}" onkeyup=${function () { self.updateBlockN(arguments[0]) }} type='text' placeholder=${'Block number'} />
<input class="${css.txinput}" id='txinput' onkeyup=${function () { self.updateTxN(arguments[0]) }} type='text' placeholder=${'Transaction index or hash'} />
<div class="${css.txinputs} p-1 input-group">
<input class="form-control" class="${css.txinput}" onkeyup=${function () { self.updateBlockN(arguments[0]) }} type='text' placeholder=${'Block number'} />
<input class="form-control" class="${css.txinput}" id='txinput' onkeyup=${function () { self.updateTxN(arguments[0]) }} type='text' placeholder=${'Transaction index or hash'} />
</div>
<div class="${css.txbuttons}">
<button id='load' class='${css.txbutton}' title='start debugging' onclick=${function () { self.submit() }}>Start debugging</button>
<button id='unload' class='${css.txbutton}' title='stop debugging' onclick=${function () { self.unload() }}>Stop</button>
<div class="${css.txbuttons} btn-group p-1">
<button class='btn btn-primary btn-sm' id='load' class='${css.txbutton}' title='start debugging' onclick=${function () { self.submit() }}>Start debugging</button>
<button class='btn btn-primary btn-sm' id='unload' class='${css.txbutton}' title='stop debugging' onclick=${function () { self.unload() }}>Stop</button>
</div>
</div>
<span id='error'></span>
......
......@@ -16,8 +16,6 @@ var DropdownPanel = require('./vmDebugger/DropdownPanel')
var css = csjs`
.asmCode {
float: left;
width: 50%;
}
.stepDetail {
}
......@@ -123,10 +121,10 @@ function VmDebugger (vmDebuggerLogic) {
}
VmDebugger.prototype.renderHead = function () {
var headView = yo`<div id='vmheadView' class=${css.vmheadView}>
<div>
<div class=${css.asmCode}>${this.asmCode.render()}</div>
<div class=${css.stepDetail}>${this.stepDetail.render()}</div>
var headView = yo`<div id='vmheadView' class="${css.vmheadView} container">
<div class="row" >
<div class="${css.asmCode} column">${this.asmCode.render()}</div>
<div class="${css.stepDetail} column">${this.stepDetail.render()}</div>
</div>
</div>`
if (!this.headView) {
......
......@@ -4,12 +4,9 @@ var yo = require('yo-yo')
var DropdownPanel = require('./DropdownPanel')
var EventManager = require('../../../../lib/events')
var csjs = require('csjs-inject')
var styleGuide = require('../../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.instructions {
${styles.rightPanel.debuggerTab.box_Debugger}
overflow-y: scroll;
max-height: 150px;
}
......@@ -43,7 +40,8 @@ CodeListView.prototype.indexChanged = function (index) {
}
}
this.itemSelected = this.codeView.children[index]
this.itemSelected.setAttribute('style', 'background-color: ' + styles.rightPanel.debuggerTab.text_BgHighlight)
this.itemSelected.style.setProperty('background-color', 'var(--info)')
this.itemSelected.style.setProperty('color', 'var(--light)')
this.itemSelected.setAttribute('selected', 'selected')
if (this.itemSelected.firstChild) {
this.itemSelected.firstChild.setAttribute('style', 'margin-left: 2px')
......
......@@ -5,13 +5,9 @@ var EventManager = require('../../../../lib/events')
var TreeView = require('../../../ui/TreeView') // TODO setup a direct reference to the UI components
var csjs = require('csjs-inject')
var styleGuide = require('../../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.title {
margin-top: 10px;
${styles.rightPanel.debuggerTab.dropdown_Debugger};
display: flex;
align-items: center;
}
......@@ -23,18 +19,16 @@ var css = csjs`
margin-left: 3px;
}
.icon {
color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_Color};
margin-right: 5%;
}
.eyeButton {
margin: 3px;
}
.eyeButton:hover {
color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor};
}
.dropdownpanel {
${styles.rightPanel.debuggerTab.dropdown_Debugger};
width: 100%;
word-break: break-all;
}
.dropdownrawcontent {
padding: 2px;
......@@ -71,7 +65,7 @@ DropdownPanel.prototype.setMessage = function (message) {
if (!this.view) return
this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none'
this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none'
this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none'
this.view.querySelector('.dropdownpanel > i').style.display = 'none'
this.message(message)
}
......@@ -79,20 +73,18 @@ DropdownPanel.prototype.setLoading = function () {
if (!this.view) return
this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none'
this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none'
this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'inline-block'
this.view.querySelector('.dropdownpanel > i').style.display = 'inline-block'
this.message('')
}
DropdownPanel.prototype.setUpdating = function () {
if (!this.view) return
this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.greyedText_color
}
DropdownPanel.prototype.update = function (_data, _header) {
if (!this.view) return
this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none'
this.view.querySelector('.dropdownpanel > i').style.display = 'none'
this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'block'
this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.mainText_Color
this.view.querySelector('.dropdownpanel .dropdownrawcontent').innerText = JSON.stringify(_data, null, '\t')
if (!this.displayContentOnly) {
this.view.querySelector('.title div.btn').style.display = 'block'
......@@ -106,11 +98,10 @@ DropdownPanel.prototype.update = function (_data, _header) {
DropdownPanel.prototype.setContent = function (node) {
if (!this.view) return
var parent = this.view.querySelector('.dropdownpanel div.dropdowncontent')
parent.replaceChild(node, parent.firstElementChild)
yo.update(this.view, this.render(null, node))
}
DropdownPanel.prototype.render = function (overridestyle) {
DropdownPanel.prototype.render = function (overridestyle, node) {
var content = yo`<div>Empty</div>`
if (this.json) {
content = this.treeView.render({})
......@@ -118,19 +109,19 @@ DropdownPanel.prototype.render = function (overridestyle) {
overridestyle === undefined ? {} : overridestyle
var self = this
var title = !self.displayContentOnly ? yo`<div class="${css.title} title">
<div class="${css.icon} fa fa-caret-right" onclick=${function () { self.toggle() }} ></div>
<div class="${css.icon} fas fa-caret-right" onclick=${function () { self.toggle() }} ></div>
<div class="${css.name}" onclick=${function () { self.toggle() }} >${this.name}</div><span class="${css.nameDetail}" onclick=${function () { self.toggle() }} ></span>
<div onclick=${function () { self.copyClipboard() }} title='raw' class="${css.eyeButton} btn fa fa-clipboard"></div>
<div onclick=${function () { self.copyClipboard() }} title='raw' class="${css.eyeButton} btn far fa-clipboard"></div>
</div>` : yo`<div></div>`
var contentNode = yo`<div class='dropdownpanel' style='display:none'>
<i class="${css.refresh} fa fa-refresh" aria-hidden="true"></i>
<div class='dropdowncontent'>${content}</div>
var contentNode = yo`<div class='dropdownpanel ${css.dropdownpanel}' style='display:none'>
<i class="${css.refresh} fas fa-sync" aria-hidden="true"></i>
<div class='dropdowncontent'>${node || content}</div>
<div class='dropdownrawcontent' style='display:none'></div>
<div class='message' style='display:none'></div>
</div>`
var view = yo`
<div>
<div class="border border-primary rounded p-1 m-1">
<style>
@-moz-keyframes spin {
to { -moz-transform: rotate(359deg); }
......@@ -162,11 +153,11 @@ DropdownPanel.prototype.toggle = function () {
var caret = this.view.querySelector('.title').firstElementChild
if (el.style.display === '') {
el.style.display = 'none'
caret.className = `${css.icon} fa fa-caret-right`
caret.className = `${css.icon} fas fa-caret-right`
this.event.trigger('hide', [])
} else {
el.style.display = ''
caret.className = `${css.icon} fa fa-caret-down`
caret.className = `${css.icon} fas fa-caret-down`
this.event.trigger('show', [])
}
}
......@@ -176,7 +167,7 @@ DropdownPanel.prototype.hide = function () {
var caret = this.view.querySelector('.title').firstElementChild
var el = this.view.querySelector('.dropdownpanel')
el.style.display = 'none'
caret.className = `${css.icon} fa fa-caret-right`
caret.className = `${css.icon} fas fa-caret-right`
this.event.trigger('hide', [])
}
......@@ -185,7 +176,7 @@ DropdownPanel.prototype.show = function () {
var caret = this.view.querySelector('.title').firstElementChild
var el = this.view.querySelector('.dropdownpanel')
el.style.display = ''
caret.className = `${css.icon} fa fa-caret-down`
caret.className = `${css.icon} fas fa-caret-down`
this.event.trigger('show', [])
}
......
'use strict'
const SourceHighlighter = require('./sourceHighlighter')
import { EditorApi } from 'remix-plugin'
const profile = {
displayName: 'source highlighters',
name: 'editor',
description: 'service - highlight source code'
}
// EditorApi:
// - methods: ['highlight', 'discardHighlight'],
class SourceHighlighters extends EditorApi {
constructor () {
super(profile)
this.highlighters = {}
}
highlight (position, filePath, hexColor) {
const { from } = this.currentRequest
try {
if (!this.highlighters[from]) this.highlighters[from] = new SourceHighlighter()
this.highlighters[from].currentSourceLocation(null)
this.highlighters[from].currentSourceLocationFromfileName(position, filePath, hexColor)
} catch (e) {
throw e
}
}
discardHighlight () {
const { from } = this.currentRequest
if (this.highlighters[from]) this.highlighters[from].currentSourceLocation(null)
}
}
module.exports = SourceHighlighters
'use strict'
var yo = require('yo-yo')
var remixLib = require('remix-lib')
var SourceMappingDecoder = remixLib.SourceMappingDecoder
var globalRegistry = require('../../global/registry')
const yo = require('yo-yo')
const remixLib = require('remix-lib')
const SourceMappingDecoder = remixLib.SourceMappingDecoder
const globalRegistry = require('../../global/registry')
var css = require('./styles/contextView-styles')
const css = require('./styles/contextView-styles')
/*
Display information about the current focused code:
......@@ -15,37 +15,38 @@ var css = require('./styles/contextView-styles')
*/
class ContextView {
constructor (opts, localRegistry) {
const self = this
self._components = {}
self._components.registry = localRegistry || globalRegistry
self.contextualListener = opts.contextualListener
self.editor = opts.editor
self._deps = {
compilersArtefacts: self._components.registry.get('compilersartefacts').api,
offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api,
config: self._components.registry.get('config').api,
fileManager: self._components.registry.get('filemanager').api
this._components = {}
this._components.registry = localRegistry || globalRegistry
this.contextualListener = opts.contextualListener
this.editor = opts.editor
this._deps = {
compilersArtefacts: this._components.registry.get('compilersartefacts').api,
offsetToLineColumnConverter: this._components.registry.get('offsettolinecolumnconverter').api,
config: this._components.registry.get('config').api,
fileManager: this._components.registry.get('filemanager').api
}
this._view
this._nodes
this._current
this.sourceMappingDecoder = new SourceMappingDecoder()
this.previousElement = null
self.contextualListener.event.register('contextChanged', nodes => {
this.contextualListener.event.register('contextChanged', nodes => {
this.show()
this._nodes = nodes
this.update()
})
this.contextualListener.event.register('stopHighlighting', () => {
})
}
render () {
var view = yo`<div class=${css.contextview}>
const view = yo`<div class="${css.contextview} ${css.contextviewcontainer} badge bg-light border-0">
<div class=${css.container}>
${this._renderTarget()}
</div>
</div>`
if (!this._view) {
this._view = view
this.hide()
}
return view
}
......@@ -65,18 +66,18 @@ class ContextView {
update () {
if (this._view) {
yo.update(this._view, this.render())
this._view.style.display = this._current ? 'block' : 'none'
}
}
_renderTarget () {
var previous = this._current
let last
const previous = this._current
if (this._nodes && this._nodes.length) {
var last = this._nodes[this._nodes.length - 1]
last = this._nodes[this._nodes.length - 1]
if (isDefinition(last)) {
this._current = last
} else {
var target = this.contextualListener.declarationOf(last)
const target = this.contextualListener.declarationOf(last)
if (target) {
this._current = target
} else {
......@@ -91,27 +92,26 @@ class ContextView {
}
_jumpToInternal (position) {
var self = this
function jumpToLine (lineColumn) {
const jumpToLine = (lineColumn) => {
if (lineColumn.start && lineColumn.start.line && lineColumn.start.column) {
self.editor.gotoLine(lineColumn.start.line, lineColumn.end.column + 1)
this.editor.gotoLine(lineColumn.start.line, lineColumn.end.column + 1)
}
}
let lastCompilationResult = self._deps.compilersArtefacts['__last']
if (lastCompilationResult && lastCompilationResult.data) {
var lineColumn = self._deps.offsetToLineColumnConverter.offsetToLineColumn(
let lastCompilationResult = this._deps.compilersArtefacts['__last']
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) {
const lineColumn = this._deps.offsetToLineColumnConverter.offsetToLineColumn(
position,
position.file,
lastCompilationResult.getSourceCode().sources,
lastCompilationResult.getAsts())
var filename = lastCompilationResult.getSourceName(position.file)
const filename = lastCompilationResult.getSourceName(position.file)
// TODO: refactor with rendererAPI.errorClick
if (filename !== self._deps.config.get('currentFile')) {
var provider = self._deps.fileManager.fileProviderOf(filename)
if (filename !== this._deps.config.get('currentFile')) {
const provider = this._deps.fileManager.fileProviderOf(filename)
if (provider) {
provider.exists(filename, (error, exist) => {
if (error) return console.log(error)
self._deps.fileManager.switchFile(filename)
this._deps.fileManager.switchFile(filename)
jumpToLine(lineColumn)
})
}
......@@ -123,14 +123,13 @@ class ContextView {
_render (node, nodeAtCursorPosition) {
if (!node) return yo`<div></div>`
var self = this
var references = self.contextualListener.referencesOf(node)
var type = (node.attributes && node.attributes.type) ? node.attributes.type : node.name
let references = this.contextualListener.referencesOf(node)
const type = (node.attributes && node.attributes.type) ? node.attributes.type : node.name
references = `${references ? references.length : '0'} reference(s)`
var ref = 0
var nodes = self.contextualListener.getActiveHighlights()
for (var k in nodes) {
let ref = 0
const nodes = this.contextualListener.getActiveHighlights()
for (const k in nodes) {
if (nodeAtCursorPosition.id === nodes[k].nodeId) {
ref = k
break
......@@ -138,44 +137,43 @@ class ContextView {
}
// JUMP BETWEEN REFERENCES
function jump (e) {
const jump = (e) => {
e.target.dataset.action === 'next' ? ref++ : ref--
if (ref < 0) ref = nodes.length - 1
if (ref >= nodes.length) ref = 0
self._jumpToInternal(nodes[ref].position)
this._jumpToInternal(nodes[ref].position)
}
function jumpTo () {
const jumpTo = () => {
if (node && node.src) {
var position = self.sourceMappingDecoder.decode(node.src)
const position = this.sourceMappingDecoder.decode(node.src)
if (position) {
self._jumpToInternal(position)
this._jumpToInternal(position)
}
}
}
return yo`<div class=${css.line}>
<div title=${type} class=${css.type}>${type}</div>
<div title=${node.attributes.name} class=${css.name}>${node.attributes.name}</div>
<i class="fa fa-share ${css.jump}" aria-hidden="true" onclick=${jumpTo}></i>
<span class=${css.referencesnb}>${references}</span>
<i data-action='previous' class="fa fa-chevron-up ${css.jump}" aria-hidden="true" onclick=${jump}></i>
<i data-action='next' class="fa fa-chevron-down ${css.jump}" aria-hidden="true" onclick=${jump}></i>
${showGasEstimation()}
</div>`
function showGasEstimation () {
const showGasEstimation = () => {
if (node.name === 'FunctionDefinition') {
var result = self.contextualListener.gasEstimation(node)
var executionCost = 'Execution cost: ' + result.executionCost + ' gas'
var codeDepositCost = 'Code deposit cost: ' + result.codeDepositCost + ' gas'
var estimatedGas = result.codeDepositCost ? `${codeDepositCost}, ${executionCost}` : `${executionCost}`
const result = this.contextualListener.gasEstimation(node)
const executionCost = 'Execution cost: ' + result.executionCost + ' gas'
const codeDepositCost = 'Code deposit cost: ' + result.codeDepositCost + ' gas'
const estimatedGas = result.codeDepositCost ? `${codeDepositCost}, ${executionCost}` : `${executionCost}`
return yo`<div class=${css.gasEstimation}>
<img class=${css.gasStationIcon} title='Gas estimation' src='assets/img/gasStation_50.png'>
${estimatedGas}
</div>`
}
}
return yo`<div class=${css.line}>${showGasEstimation()}
<div title=${type} class=${css.type}>${type}</div>
<div title=${node.attributes.name} class=${css.name}>${node.attributes.name}</div>
<i class="fas fa-share ${css.jump}" aria-hidden="true" onclick=${jumpTo}></i>
<span class=${css.referencesnb}>${references}</span>
<i data-action='previous' class="fas fa-chevron-up ${css.jump}" aria-hidden="true" onclick=${jump}></i>
<i data-action='next' class="fas fa-chevron-down ${css.jump}" aria-hidden="true" onclick=${jump}></i>
</div>`
}
}
......
This diff is collapsed.
This diff is collapsed.
'use strict'
var csjs = require('csjs-inject')
var globlalRegistry = require('../../global/registry')
var styleGuide = require('../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
const csjs = require('csjs-inject')
const globlalRegistry = require('../../global/registry')
class SourceHighlighter {
constructor (localRegistry) {
const self = this
self._components = {}
self._components.registry = localRegistry || globlalRegistry
this._components = {}
this._components.registry = localRegistry || globlalRegistry
// dependencies
self._deps = {
editor: self._components.registry.get('editor').api,
config: self._components.registry.get('config').api,
fileManager: self._components.registry.get('filemanager').api,
compilerArtefacts: self._components.registry.get('compilersartefacts').api
this._deps = {
editor: this._components.registry.get('editor').api,
config: this._components.registry.get('config').api,
fileManager: this._components.registry.get('filemanager').api,
compilerArtefacts: this._components.registry.get('compilersartefacts').api
}
this.statementMarker = null
this.fullLineMarker = null
......@@ -26,7 +23,7 @@ class SourceHighlighter {
if (this.fullLineMarker) this._deps.editor.removeMarker(this.fullLineMarker, this.source)
let lastCompilationResult = this._deps.compilerArtefacts['__last']
if (location && location.file !== undefined && lastCompilationResult) {
var path = lastCompilationResult.getSourceName(location.file)
const path = lastCompilationResult.getSourceName(location.file)
if (path) {
this.currentSourceLocationFromfileName(lineColumnPos, path)
}
......@@ -45,21 +42,24 @@ class SourceHighlighter {
this._deps.fileManager.switchFile(this.source)
}
var css = csjs`
const css = csjs`
.highlightcode {
position:absolute;
z-index:20;
background-color: ${style || styles.editor.backgroundColor_DebuggerMode};
background-color: ${style || 'var(--info)'};
}
.highlightcode_fullLine {
position:absolute;
z-index:20;
background-color: ${style || styles.editor.backgroundColor_DebuggerMode};
opacity: 0.5;
background-color: ${style || 'var(--info)'};
}
.customBackgroundColor {
background-color: ${style || 'var(--info)'};
}
`
this.statementMarker = this._deps.editor.addMarker(lineColumnPos, this.source, css.highlightcode)
this.statementMarker = this._deps.editor.addMarker(lineColumnPos, this.source, css.highlightcode.className + ' ' + css.customBackgroundColor.className)
this._deps.editor.scrollToLine(lineColumnPos.start.line, true, true, function () {})
if (lineColumnPos.start.line === lineColumnPos.end.line) {
......@@ -72,7 +72,7 @@ class SourceHighlighter {
line: lineColumnPos.start.line + 1,
column: 0
}
}, this.source, css.highlightcode_fullLine)
}, this.source, css.highlightcode_fullLine.className)
}
}
}
......
var csjs = require('csjs-inject')
var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.contextview {
opacity : 0.8;
}
opacity : 1;
position : relative;
height : 20px;
}
.container {
padding : 1px 15px;
}
......@@ -16,8 +16,7 @@ var css = csjs`
text-overflow : ellipsis;
overflow : hidden;
white-space : nowrap;
color : ${styles.editor.text_Primary};
font-size : 11px;
font-size : 13px;
}
.type {
font-style : italic;
......@@ -29,17 +28,16 @@ var css = csjs`
.jump {
cursor : pointer;
margin : 0 5px;
color : ${styles.editor.icon_Color_Editor};
}
.jump:hover {
color : ${styles.editor.icon_HoverColor_Editor};
color : var(--secondary);
}
.referencesnb {
float : right;
margin-left : 15px;
}
.gasEstimation {
margin-left: 15px;
margin-right: 15px;
display: flex;
align-items: center;
}
......@@ -47,6 +45,11 @@ var css = csjs`
height: 13px;
margin-right: 5px;
}
.contextviewcontainer{
z-index : 50;
border-radius : 1px;
border : 2px solid var(--secondary);
}
`
module.exports = css
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var styleGuide = require('../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
const copyToClipboard = require('../ui/copy-to-clipboard')
var css = csjs`
.txInfoBox {
${styles.rightPanel.compileTab.box_CompileContainer}; // add askToConfirmTXContainer to Remix and then replace this styling
}
.wrapword {
white-space: pre-wrap; /* Since CSS 2.1 */
......@@ -38,11 +36,11 @@ function confirmDialog (tx, amount, gasEstimation, self, newGasPriceCb, initialP
<div>Gas price: <input id='gasprice' oninput=${onGasPriceChange} /> Gwei <span> (visit <a target='_blank' href='https://ethgasstation.info'>ethgasstation.info</a> to get more info about gas price)</span></div>
<div>Max transaction fee:<span id='txfee'></span></div>
<div>Data:</div>
<pre class=${css.wrapword}>${tx.data}</pre>
<pre class=${css.wrapword}>${tx.data && tx.data.length > 50 ? tx.data.substring(0, 49) + '...' : tx.data} ${copyToClipboard(() => { return tx.data })}</pre>
</div>
<div class=${css.checkbox}>
<input id='confirmsetting' type="checkbox">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Do not ask for confirmation again. (the setting will not be persisted for the next page reload)
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i> Do not ask for confirmation again. (the setting will not be persisted for the next page reload)
</div>
</div>
`
......
......@@ -5,8 +5,6 @@ var copyToClipboard = require('../ui/copy-to-clipboard')
// -------------- styling ----------------------
var csjs = require('csjs-inject')
var remixLib = require('remix-lib')
var styleGuide = require('../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var EventManager = require('../../lib/events')
var helper = require('../../lib/helper')
......@@ -26,14 +24,14 @@ var css = csjs`
opacity: 0.8;
}
.arrow {
color: ${styles.terminal.icon_Color_Menu};
color: var(--text-info);
font-size: 20px;
cursor: pointer;
display: flex;
margin-left: 10px;
}
.arrow:hover {
color: ${styles.terminal.icon_HoverColor_Menu};
color: var(--secondary);
}
.txLog {
}
......@@ -44,28 +42,27 @@ var css = csjs`
float: left;
}
.succeeded {
color: ${styles.terminal.icon_Color_Log_Succeed};
color: var(--success);
}
.failed {
color: ${styles.terminal.icon_Color_Log_Failed};
color: var(--danger);
}
.notavailable {
}
.call {
font-size: 7px;
background-color: ${styles.terminal.icon_BackgroundColor_Log_Call};
border-radius: 50%;
min-width: 20px;
min-height: 20px;
display: flex;
justify-content: center;
align-items: center;
color: ${styles.terminal.icon_Color_Log_Call};
color: var(--text-info);
text-transform: uppercase;
font-weight: bold;
}
.txItem {
color: ${styles.terminal.text_Primary};
color: var(--text-info);
margin-right: 5px;
float: left;
}
......@@ -73,7 +70,7 @@ var css = csjs`
font-weight: bold;
}
.tx {
color: ${styles.terminal.text_Title_TransactionLog};
color: var(--text-info);
font-weight: bold;
float: left;
margin-right: 10px;
......@@ -83,8 +80,8 @@ var css = csjs`
.td {
border-collapse: collapse;
font-size: 10px;
color: ${styles.terminal.text_Primary};
border: 1px solid ${styles.terminal.text_Secondary};
color: var(--text-info);
border: 1px solid var(--text-info);
}
#txTable {
margin-top: 1%;
......@@ -110,12 +107,7 @@ var css = csjs`
margin-left: auto;
}
.debug {
${styles.terminal.button_Log_Debug}
width: 55px;
min-width: 55px;
min-height: 20px;
max-height: 20px;
font-size: 11px;
white-space: nowrap;
}
.debug:hover {
opacity: 0.8;
......@@ -142,8 +134,7 @@ class TxLogger {
editorPanel: this._components.registry.get('editorpanel').api,
txListener: this._components.registry.get('txlistener').api,
eventsDecoder: this._components.registry.get('eventsdecoder').api,
compilersArtefacts: this._components.registry.get('compilersartefacts').api,
app: this._components.registry.get('app').api
compilersArtefacts: this._components.registry.get('compilersartefacts').api
}
this.logKnownTX = this._deps.editorPanel.registerCommand('knownTransaction', (args, cmds, append) => {
......@@ -171,24 +162,8 @@ class TxLogger {
append(el)
}, { activate: true })
this._deps.editorPanel.event.register('terminalFilterChanged', (type, label) => {
if (type === 'deselect') {
if (label === 'only remix transactions') {
this._deps.editorPanel.updateTerminalFilter({ type: 'select', value: 'unknownTransaction' })
} else if (label === 'all transactions') {
this._deps.editorPanel.updateTerminalFilter({ type: 'deselect', value: 'unknownTransaction' })
}
} else if (type === 'select') {
if (label === 'only remix transactions') {
this._deps.editorPanel.updateTerminalFilter({ type: 'deselect', value: 'unknownTransaction' })
} else if (label === 'all transactions') {
this._deps.editorPanel.updateTerminalFilter({ type: 'select', value: 'unknownTransaction' })
}
}
})
this._deps.txListener.event.register('newBlock', (block) => {
if (!block.transactions.length) {
if (!block.transactions || block.transactions && !block.transactions.length) {
this.logEmptyBlock({ block: block })
}
})
......@@ -200,6 +175,9 @@ class TxLogger {
this._deps.txListener.event.register('newCall', (tx) => {
log(this, tx, null)
})
this._deps.editorPanel.updateTerminalFilter({ type: 'select', value: 'unknownTransaction' })
this._deps.editorPanel.updateTerminalFilter({ type: 'select', value: 'knownTransaction' })
}
}
......@@ -208,7 +186,7 @@ function debug (e, data, self) {
if (data.tx.isCall && data.tx.envMode !== 'vm') {
modalDialog.alert('Cannot debug this call. Debugging calls is only possible in JavaScript VM mode.')
} else {
self._deps.app.startdebugging(data.tx.hash)
self.event.trigger('debuggingRequested', [data.tx.hash])
}
}
......@@ -241,9 +219,9 @@ function renderKnownTransaction (self, data) {
${checkTxStatus(data.receipt, txType)}
${context(self, {from, to, data})}
<div class=${css.buttons}>
<div class=${css.debug} onclick=${(e) => debug(e, data, self)}>Debug</div>
<button class="${css.debug} btn btn-primary btn-sm" onclick=${(e) => debug(e, data, self)}>Debug</div>
</div>
<i class="${css.arrow} fa fa-angle-down"></i>
<i class="${css.arrow} fas fa-angle-down"></i>
</div>
</span>
`
......@@ -267,9 +245,9 @@ function renderCall (self, data) {
<div class=${css.txItem}><span class=${css.txItemTitle}>data:</span> ${input}</div>
</span>
<div class=${css.buttons}>
<div class=${css.debug} onclick=${(e) => debug(e, data, self)}>Debug</div>
<div class="${css.debug} btn btn-primary btn-sm" onclick=${(e) => debug(e, data, self)}>Debug</div>
</div>
<i class="${css.arrow} fa fa-angle-down"></i>
<i class="${css.arrow} fas fa-angle-down"></i>
</div>
</span>
`
......@@ -287,9 +265,9 @@ function renderUnknownTransaction (self, data) {
${checkTxStatus(data.receipt || data.tx, txType)}
${context(self, {from, to, data})}
<div class=${css.buttons}>
<div class=${css.debug} onclick=${(e) => debug(e, data, self)}>Debug</div>
<div class="${css.debug} btn btn-primary btn-sm" onclick=${(e) => debug(e, data, self)}>Debug</div>
</div>
<i class="${css.arrow} fa fa-angle-down"></i>
<i class="${css.arrow} fas fa-angle-down"></i>
</div>
</span>
`
......@@ -305,14 +283,14 @@ function renderEmptyBlock (self, data) {
function checkTxStatus (tx, type) {
if (tx.status === '0x1') {
return yo`<i class="${css.txStatus} ${css.succeeded} fa fa-check-circle"></i>`
return yo`<i class="${css.txStatus} ${css.succeeded} fas fa-check-circle"></i>`
}
if (type === 'call' || type === 'unknownCall') {
return yo`<i class="${css.txStatus} ${css.call}">call</i>`
} else if (tx.status === '0x0') {
return yo`<i class="${css.txStatus} ${css.failed} fa fa-times-circle"></i>`
return yo`<i class="${css.txStatus} ${css.failed} fas fa-times-circle"></i>`
} else {
return yo`<i class="${css.txStatus} ${css.notavailable} fa fa-circle-thin" title='Status not available' ></i>`
return yo`<i class="${css.txStatus} ${css.notavailable} fas fa-circle-thin" title='Status not available' ></i>`
}
}
......@@ -379,8 +357,8 @@ function txDetails (e, tx, data, obj) {
var to = obj.to
var log = document.querySelector(`#${tx.id} [class^='log']`)
var arrow = document.querySelector(`#${tx.id} [class^='arrow']`)
var arrowUp = yo`<i class="${css.arrow} fa fa-angle-up"></i>`
var arrowDown = yo`<i class="${css.arrow} fa fa-angle-down"></i>`
var arrowUp = yo`<i class="${css.arrow} fas fa-angle-up"></i>`
var arrowDown = yo`<i class="${css.arrow} fas fa-angle-down"></i>`
if (table && table.parentNode) {
tx.removeChild(table)
log.removeChild(arrow)
......
......@@ -2,21 +2,30 @@
var EventManager = require('../../lib/events')
function FilesTree (name, storage) {
var self = this
var event = new EventManager()
this.event = event
this.type = name
this.structFile = '.' + name + '.tree'
this.tree = {}
this.exists = function (path, cb) {
import { BaseApi } from 'remix-plugin'
class FilesTree extends BaseApi {
constructor (name, storage) {
super({
name: name,
methods: ['get', 'set', 'remove'],
description:
'service - read/write file to the `config` explorer without need of additionnal permission.'
})
this.event = new EventManager()
this.storage = storage
this.type = name
this.structFile = '.' + name + '.tree'
this.tree = {}
}
exists (path, cb) {
cb(null, this._exists(path))
}
function updateRefs (path, type) {
updateRefs (path, type) {
var split = path.split('/') // this should be unprefixed path
var crawlpath = self.tree
var crawlpath = this.tree
var intermediatePath = ''
split.forEach((pathPart, index) => {
intermediatePath += pathPart
......@@ -30,91 +39,94 @@ function FilesTree (name, storage) {
delete crawlpath[intermediatePath]
}
})
storage.set(self.structFile, JSON.stringify(self.tree))
this.storage.set(this.structFile, JSON.stringify(this.tree))
}
this._exists = function (path) {
_exists (path) {
var unprefixedpath = this.removePrefix(path)
return storage.exists(unprefixedpath)
return this.storage.exists(unprefixedpath)
}
this.init = function (cb) {
var tree = storage.get(this.structFile)
init (cb) {
var tree = this.storage.get(this.structFile)
this.tree = tree ? JSON.parse(tree) : {}
if (cb) cb()
}
this.get = function (path, cb) {
get (path, cb) {
var unprefixedpath = this.removePrefix(path)
var content = storage.get(unprefixedpath)
var content = this.storage.get(unprefixedpath)
if (cb) {
cb(null, content)
}
return content
}
this.set = function (path, content, cb) {
set (path, content, cb) {
var unprefixedpath = this.removePrefix(path)
updateRefs(unprefixedpath, 'add')
var exists = storage.exists(unprefixedpath)
if (!storage.set(unprefixedpath, content)) {
this.updateRefs(unprefixedpath, 'add')
var exists = this.storage.exists(unprefixedpath)
if (!this.storage.set(unprefixedpath, content)) {
if (cb) cb('error updating ' + path)
return false
}
if (!exists) {
event.trigger('fileAdded', [this.type + '/' + unprefixedpath, false])
this.event.trigger('fileAdded', [this.type + '/' + unprefixedpath, false])
} else {
event.trigger('fileChanged', [this.type + '/' + unprefixedpath])
this.event.trigger('fileChanged', [this.type + '/' + unprefixedpath])
}
if (cb) cb()
return true
}
this.addReadOnly = function (path, content) {
addReadOnly (path, content) {
return this.set(path, content)
}
this.isReadOnly = function (path) {
isReadOnly (path) {
return false
}
this.remove = function (path) {
remove (path) {
var unprefixedpath = this.removePrefix(path)
updateRefs(unprefixedpath, 'remove')
this.updateRefs(unprefixedpath, 'remove')
if (!this._exists(unprefixedpath)) {
return false
}
if (!storage.remove(unprefixedpath)) {
if (!this.storage.remove(unprefixedpath)) {
return false
}
event.trigger('fileRemoved', [this.type + '/' + unprefixedpath])
this.event.trigger('fileRemoved', [this.type + '/' + unprefixedpath])
return true
}
this.rename = function (oldPath, newPath, isFolder) {
rename (oldPath, newPath, isFolder) {
var unprefixedoldPath = this.removePrefix(oldPath)
var unprefixednewPath = this.removePrefix(newPath)
updateRefs(unprefixedoldPath, 'remove')
updateRefs(unprefixednewPath, 'add')
if (storage.exists(unprefixedoldPath)) {
if (!storage.rename(unprefixedoldPath, unprefixednewPath)) {
this.updateRefs(unprefixedoldPath, 'remove')
this.updateRefs(unprefixednewPath, 'add')
if (this.storage.exists(unprefixedoldPath)) {
if (!this.storage.rename(unprefixedoldPath, unprefixednewPath)) {
return false
}
event.trigger('fileRenamed', [this.type + '/' + unprefixedoldPath, this.type + '/' + unprefixednewPath, isFolder])
this.event.trigger('fileRenamed', [
this.type + '/' + unprefixedoldPath,
this.type + '/' + unprefixednewPath,
isFolder
])
return true
}
return false
}
this.resolveDirectory = function (path, callback) {
var self = this
resolveDirectory (path, callback) {
if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [self.type]: { } })
if (!path) return callback(null, { [this.type]: {} })
var tree = {}
path = self.removePrefix(path)
path = this.removePrefix(path)
var split = path.split('/') // this should be unprefixed path
var crawlpath = self.tree
var crawlpath = this.tree
split.forEach((pathPart, index) => {
if (crawlpath[pathPart]) crawlpath = crawlpath[pathPart]
})
......@@ -125,7 +137,7 @@ function FilesTree (name, storage) {
callback(null, tree)
}
this.removePrefix = function (path) {
removePrefix (path) {
path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path
if (path[0] === '/') return path.substring(1)
return path
......
This diff is collapsed.
This diff is collapsed.
let globalRegistry = require('../../global/registry')
import { BaseApi } from 'remix-plugin'
var yo = require('yo-yo')
var modalDialog = require('../ui/modaldialog')
var modalDialogCustom = require('../ui/modal-dialog-custom')
var csjs = require('csjs-inject')
var css = csjs`
.dialog {
display: flex;
flex-direction: column;
}
.dialogParagraph {
margin-bottom: 2em;
word-break: break-word;
}
`
const profile = {
name: 'remixd',
methods: [],
events: [],
description: 'using Remixd daemon, allow to access file system',
kind: 'other'
}
export class RemixdHandle extends BaseApi {
constructor (fileSystemExplorer, locahostProvider) {
super(profile)
this.fileSystemExplorer = fileSystemExplorer
this.locahostProvider = locahostProvider
}
deactivate () {
this.locahostProvider.close((error) => {
if (error) console.log(error)
})
}
activate () {
this.connectToLocalhost()
}
canceled () {
let appManager = globalRegistry.get('appmanager').api
appManager.ensureDeactivated('remixd')
}
/**
* connect to localhost if no connection and render the explorer
* disconnect from localhost if connected and remove the explorer
*
* @param {String} txHash - hash of the transaction
*/
connectToLocalhost () {
if (this.locahostProvider.isConnected()) {
this.locahostProvider.close((error) => {
if (error) console.log(error)
})
} else {
modalDialog(
'Connect to localhost',
remixdDialog(),
{ label: 'Connect',
fn: () => {
this.locahostProvider.init((error) => {
if (error) {
console.log(error)
modalDialogCustom.alert(
'Cannot connect to the remixd daemon.' +
'Please make sure you have the remixd running in the background.'
)
this.canceled()
} else {
this.fileSystemExplorer.ensureRoot()
}
})
}
},
{ label: 'Cancel',
fn: () => {
this.canceled()
}
}
)
}
}
}
function remixdDialog () {
return yo`
<div class=${css.dialog}>
<div class=${css.dialogParagraph}>Interact with your file system from Remix. Click connect and find shared folder in the Remix file explorer (under localhost).
Before you get started, check out <a target="_blank" href="https://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html">Tutorial_remixd_filesystem</a>
to find out how to run Remixd.
</div>
<div class=${css.dialogParagraph}>Connection will start a session between <em>${window.location.href}</em> and your local file system <i>ws://127.0.0.1:65520</i>
so please make sure your system is secured enough (port 65520 neither opened nor forwarded).
<i class="fas fa-link"></i> will show you current connection status.
</div>
<div class=${css.dialogParagraph}>This feature is still in Alpha, so we recommend you to keep a copy of the shared folder.</div>
</div>
`
}
var csjs = require('csjs-inject')
var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.label {
margin-top : 4px
}
.fileexplorer {
box-sizing : border-box;
}
......@@ -15,13 +16,26 @@ var css = csjs`
cursor : pointer;
}
.file {
color : ${styles.leftPanel.text_Teriary};
padding : 4px;
}
.newFile {
padding-right : 10px;
}
.newFile i {
cursor : pointer;
}
.newFile:hover {
transform : scale(1.3);
}
.menu {
margin-left : 20px;
}
.items {
display : inline
}
.hasFocus {
background-color : ${styles.leftPanel.backgroundColor_FileExplorer};
}
.rename {
background-color : ${styles.leftPanel.backgroundColor_Panel};
}
.remove {
margin-left : auto;
......
This diff is collapsed.
This diff is collapsed.
const yo = require('yo-yo')
const csjs = require('csjs-inject')
const EventManager = require('../../lib/events')
var globalRegistry = require('../../global/registry')
const styleguide = require('../ui/styles-guide/theme-chooser')
const TabbedMenu = require('../tabs/tabbed-menu')
const PluginTab = require('../tabs/plugin-tab')
const DraggableContent = require('../ui/draggableContent')
const styles = styleguide.chooser()
const css = csjs`
.righthandpanel {
display : flex;
flex-direction : column;
top : 0;
right : 0;
bottom : 0;
box-sizing : border-box;
overflow : hidden;
height : 100%;
}
.header {
height : 100%;
}
.dragbar {
position : absolute;
width : ${styles.rightPanel.dragbarWidth};
top : 3em;
bottom : 0;
cursor : col-resize;
background-color : ${styles.rightPanel.dragbarBackgroundColor};
z-index : 999;
border-left : 2px solid ${styles.rightPanel.bar_Dragging};
}
.ghostbar {
width : 3px;
background-color : ${styles.rightPanel.bar_Ghost};
opacity : 0.5;
position : absolute;
cursor : col-resize;
z-index : 9999;
top : 0;
bottom : 0;
}
`
class RighthandPanel {
constructor ({pluginManager, tabs}, localRegistry) {
const self = this
self._components = {}
self._components.registry = localRegistry || globalRegistry
self._components.registry.put({api: this, name: 'righthandpanel'})
self.event = new EventManager()
self._view = {
element: null,
tabbedMenu: null,
tabbedMenuViewport: null,
dragbar: null
}
var tabbedMenu = new TabbedMenu(self._components.registry)
self._components = {
tabbedMenu: tabbedMenu,
tabs
}
self._components.tabs.settings.event.register('plugin-loadRequest', json => {
self.loadPlugin(json)
})
self.loadPlugin = function (json) {
var modal = new DraggableContent(() => {
pluginManager.unregister(json)
})
var tab = new PluginTab(json)
var content = tab.render()
document.querySelector('body').appendChild(modal.render(json.title, json.url, content))
pluginManager.register(json, modal, content)
}
self._view.dragbar = yo`<div id="dragbar" class=${css.dragbar}></div>`
self._view.element = yo`
<div id="righthand-panel" class=${css.righthandpanel}>
${self._view.dragbar}
<div id="header" class=${css.header}>
${self._components.tabbedMenu.render()}
${self._components.tabbedMenu.renderViewport()}
</div>
</div>`
const { compile, run, settings, analysis, debug, support, test } = tabs
self._components.tabbedMenu.addTab('Compile', 'compileView', compile.render())
self._components.tabbedMenu.addTab('Run', 'runView', run.render())
self._components.tabbedMenu.addTab('Analysis', 'staticanalysisView', analysis.render())
self._components.tabbedMenu.addTab('Testing', 'testView', test.render())
self._components.tabbedMenu.addTab('Debugger', 'debugView', debug.render())
self._components.tabbedMenu.addTab('Settings', 'settingsView', settings.render())
self._components.tabbedMenu.addTab('Support', 'supportView', support.render())
self._components.tabbedMenu.selectTabByTitle('Compile')
}
render () {
const self = this
if (self._view.element) return self._view.element
return self._view.element
}
debugger () {
return this._components.tabs.debug.debugger()
}
focusOn (x) {
if (this._components.tabbedMenu) this._components.tabbedMenu.selectTabByClassName(x)
}
init () {
// @TODO: init is for resizable drag bar only and should be refactored in the future
const self = this
const limit = 60
self._view.dragbar.addEventListener('mousedown', mousedown)
const ghostbar = yo`<div class=${css.ghostbar}></div>`
function mousedown (event) {
event.preventDefault()
if (event.which === 1) {
moveGhostbar(event)
document.body.appendChild(ghostbar)
document.addEventListener('mousemove', moveGhostbar)
document.addEventListener('mouseup', removeGhostbar)
document.addEventListener('keydown', cancelGhostbar)
}
}
function cancelGhostbar (event) {
if (event.keyCode === 27) {
document.body.removeChild(ghostbar)
document.removeEventListener('mousemove', moveGhostbar)
document.removeEventListener('mouseup', removeGhostbar)
document.removeEventListener('keydown', cancelGhostbar)
}
}
function getPosition (event) {
const lhp = window['filepanel'].offsetWidth
const max = document.body.offsetWidth - limit
var newpos = (event.pageX > max) ? max : event.pageX
newpos = (newpos > (lhp + limit)) ? newpos : lhp + limit
return newpos
}
function moveGhostbar (event) { // @NOTE VERTICAL ghostbar
ghostbar.style.left = getPosition(event) + 'px'
}
function removeGhostbar (event) {
document.body.removeChild(ghostbar)
document.removeEventListener('mousemove', moveGhostbar)
document.removeEventListener('mouseup', removeGhostbar)
document.removeEventListener('keydown', cancelGhostbar)
self.event.trigger('resize', [document.body.offsetWidth - getPosition(event)])
}
}
}
module.exports = RighthandPanel
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var cssTabs = yo`
<style>
#files .file {
padding: 0 0.6em;
box-sizing: border-box;
background-color: ${styles.editor.backgroundColor_Tabs_Highlights};
cursor: pointer;
margin-right: 10px;
margin-top: 5px;
position: relative;
display: table-cell;
text-align: center;
vertical-align: middle;
color: ${styles.editor.text_Teriary};
}
#files .file.active {
color: ${styles.editor.text_Primary};
font-weight: bold;
border-bottom: 0 none;
padding-right: 1.5em;
}
#files .file .remove {
font-size: 12px;
display: flex;
color: ${styles.editor.text_Primary};
position: absolute;
top: -7px;
right: 5px;
display: none;
}
#files .file input {
background-color: ${styles.colors.transparent};
border: 0 none;
border-bottom: 1px dotted ${styles.editor.text_Primary};
line-height: 1em;
margin: 0.5em 0;
}
#files .file.active .remove {
display: inline-block;
color: ${styles.editor.text_Primary};
}
</style>
`
var css = csjs`
.editorpanel {
display : flex;
flex-direction : column;
height : 100%;
}
.tabsbar {
background-color : ${styles.editor.backgroundColor_Panel};
display : flex;
overflow : hidden;
height : 30px;
}
.tabs {
position : relative;
display : flex;
margin : 0;
left : 10px;
margin-right : 10px;
width : 100%;
overflow : hidden;
}
.files {
display : flex;
position : relative;
list-style : none;
margin : 0;
font-size : 15px;
height : 2.5em;
box-sizing : border-box;
line-height : 2em;
top : 0;
border-bottom : 0 none;
}
.changeeditorfontsize {
margin : 0;
font-size : 9px;
margin-top : 0.5em;
}
.changeeditorfontsize i {
cursor : pointer;
display : block;
color : ${styles.editor.icon_Color_Editor};
}
.changeeditorfontsize i {
cursor : pointer;
}
.changeeditorfontsize i:hover {
color : ${styles.editor.icon_HoverColor_Editor};
}
.buttons {
display : flex;
flex-direction : row;
justify-content : space-around;
align-items : center;
min-width : 45px;
}
.toggleLHP {
display : flex;
padding : 10px;
width : 100%;
font-weight : bold;
color : ${styles.leftPanel.icon_Color_TogglePanel};
}
.toggleLHP i {
cursor : pointer;
font-size : 14px;
font-weight : bold;
}
.toggleLHP i:hover {
color : ${styles.leftPanel.icon_HoverColor_TogglePanel};
}
.scroller {
position : absolute;
z-index : 999;
text-align : center;
cursor : pointer;
vertical-align : middle;
background-color : ${styles.colors.general_BackgroundColor};
height : 100%;
font-size : 1.3em;
color : orange;
}
.scrollerright {
right : 0;
margin-right : 15px;
}
.scrollerleft {
left : 0;
}
.toggleRHP {
margin : 0.5em;
font-weight : bold;
color : ${styles.rightPanel.icon_Color_TogglePanel};
right : 0;
}
.toggleRHP i {
cursor : pointer;
font-size : 14px;
font-weight : bold;
}
.toggleRHP i:hover {
color : ${styles.rightPanel.icon_HoverColor_TogglePanel};
}
.show {
opacity : 1;
transition : .3s opacity ease-in;
}
.hide {
opacity : 0;
pointer-events : none;
transition : .3s opacity ease-in;
}
.content {
position : relative;
display : flex;
flex-direction : column;
height : 100%;
width : 100%;
}
.contextviewcontainer{
width : 100%;
height : 20px;
background-color : ${styles.editor.backgroundColor_Tabs_Highlights};
}
`
module.exports = {
cssTabs: cssTabs,
css: css
}
var csjs = require('csjs-inject')
var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.container {
......@@ -15,22 +13,8 @@ var css = csjs`
flex-direction : column;
position : relative;
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 : ${styles.colors.orange};
padding-left : 6px;
padding-top : 6px;
}
.gist {
padding : 10px;
......@@ -57,49 +41,25 @@ var css = csjs`
cursor : pointer;
}
.connectToLocalhost i:hover {
color : ${styles.colors.orange};
color : var(--secondary)
}
.uploadFile {
padding : 10px;
}
.uploadFile label:hover {
color : ${styles.colors.orange};
color : var(--secondary)
}
.uploadFile label {
cursor : pointer;
}
.treeview {
background-color : ${styles.colors.general_BackgroundColor};
}
.treeviews {
overflow-y : auto;
}
.dragbar {
position : absolute;
top : 29px;
width : 0.5em;
right : 0;
bottom : 0;
cursor : col-resize;
z-index : 999;
border-right : ${styles.leftPanel.dragbarBorderRight};
}
.ghostbar {
width : 3px;
background-color : ${styles.colors.lightBlue};
opacity : 0.5;
position : absolute;
cursor : col-resize;
z-index : 9999;
top : 0;
bottom : 0;
}
}
.dialog {
display: flex;
flex-direction: column;
}
.dialogParagraph {
${styles.infoTextBox}
margin-bottom: 2em;
word-break: break-word;
}
......
This diff is collapsed.
var yo = require('yo-yo')
var $ = require('jquery')
const EventEmitter = require('events')
require('remix-tabs')
export class TabProxy {
constructor (fileManager, editor, appStore, appManager) {
this.event = new EventEmitter()
this.fileManager = fileManager
this.appManager = appManager
this.editor = editor
this.data = {}
this._view = {}
this._handlers = {}
fileManager.events.on('fileRemoved', (name) => {
this.removeTab(name)
})
fileManager.events.on('fileClosed', (name) => {
this.removeTab(name)
})
fileManager.events.on('currentFileChanged', (file) => {
if (this._handlers[file]) {
this._view.filetabs.activateTab(file)
return
}
this.addTab(file, '', () => {
this.fileManager.switchFile(file)
this.event.emit('switchFile', file)
},
() => {
this.fileManager.closeFile(file)
this.event.emit('closeFile', file)
})
})
fileManager.events.on('fileRenamed', (oldName, newName) => {
this.removeTab(oldName)
this.addTab(newName, '', () => {
this.fileManager.switchFile(newName)
this.event.emit('switchFile', newName)
},
() => {
this.fileManager.closeFile(newName)
this.event.emit('closeFile', newName)
})
})
appStore.event.on('activate', (name) => {
const { profile } = appStore.getOne(name)
if (profile.location === 'mainPanel') {
this.addTab(
name,
profile.displayName,
() => this.event.emit('switchApp', name),
() => {
this.event.emit('closeApp', name)
this.appManager.deactivateOne(name)
}
)
this.switchTab(name)
}
})
appStore.event.on('deactivate', (name) => {
this.removeTab(name)
})
}
switchTab (tabName) {
if (this._handlers[tabName]) {
this._handlers[tabName].switchTo()
this._view.filetabs.activateTab(tabName)
}
}
switchNextTab () {
const active = this._view.filetabs.active
if (active && this._handlers[active]) {
const handlers = Object.keys(this._handlers)
let i = handlers.indexOf(active)
if (i >= 0) {
i = handlers[i + 1] ? i + 1 : 0
this.switchTab(handlers[i])
}
}
}
switchPreviousTab () {
const active = this._view.filetabs.active
if (active && this._handlers[active]) {
const handlers = Object.keys(this._handlers)
let i = handlers.indexOf(active)
if (i >= 0) {
i = handlers[i - 1] ? i - 1 : handlers.length - 1
this.switchTab(handlers[i])
}
}
}
showTab (name) {
this._view.filetabs.activateTab(name)
}
addTab (name, title, switchTo, close, kind) {
if (this._handlers[name]) return
var slash = name.split('/')
if (!title) {
title = name.indexOf('/') !== -1 ? slash[slash.length - 1] : name
}
this._view.filetabs.addTab({
id: name,
title,
icon: '',
tooltip: name
})
this._handlers[name] = { switchTo, close }
}
removeTab (name) {
this._view.filetabs.removeTab(name)
delete this._handlers[name]
}
addHandler (type, fn) {
this.handlers[type] = fn
}
renderTabsbar () {
this._view.filetabs = yo`<remix-tabs></remix-tabs>`
this._view.filetabs.addEventListener('tabClosed', (event) => {
if (this._handlers[event.detail]) this._handlers[event.detail].close()
})
this._view.filetabs.addEventListener('tabActivated', (event) => {
if (this._handlers[event.detail]) this._handlers[event.detail].switchTo()
})
this._view.filetabs.canAdd = false
this._view.tabs = yo`
<div style="width: 100%; height: 100%;">
${this._view.filetabs}
</div>
`
let tabsbar = yo`
<div class="d-flex align-items-center" style="max-height: 35px; height: 100%">
${this._view.tabs}
</div>
`
// tabs
var $filesEl = $(this._view.filetabs)
// Switch tab
var self = this
$filesEl.on('click', '.file:not(.active)', function (ev) {
ev.preventDefault()
var name = $(this).find('.name').text()
self._handlers[name].switchTo()
return false
})
// Remove current tab
$filesEl.on('click', '.file .remove', function (ev) {
ev.preventDefault()
var name = $(this).parent().find('.name').text()
self._handlers[name].close()
return false
})
return tabsbar
}
}
This diff is collapsed.
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);
}
}return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
};
}();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var RemixExtension = function () {
function RemixExtension() {
var _this = this;
_classCallCheck(this, RemixExtension);
this._notifications = {};
this._pendingRequests = {};
this._id = 0;
window.addEventListener('message', function (event) {
return _this._newMessage(event);
}, false);
}
_createClass(RemixExtension, [{
key: 'listen',
value: function listen(key, type, callback) {
if (!this._notifications[key]) this._notifications[key] = {};
this._notifications[key][type] = callback;
}
}, {
key: 'call',
value: function call(key, type, params, callback) {
this._id++;
this._pendingRequests[this._id] = callback;
window.parent.postMessage(JSON.stringify({
action: 'request',
key: key,
type: type,
value: params,
id: this._id
}), '*');
}
}, {
key: '_newMessage',
value: function _newMessage(event) {
if (!event.data) return;
if (typeof event.data !== 'string') return;
var msg;
try {
msg = JSON.parse(event.data);
} catch (e) {
return console.log('unable to parse data');
}
var _msg = msg,
action = _msg.action,
key = _msg.key,
type = _msg.type,
value = _msg.value;
if (action === 'notification') {
if (this._notifications[key] && this._notifications[key][type]) {
this._notifications[key][type](value);
}
} else if (action === 'response') {
var _msg2 = msg,
id = _msg2.id,
error = _msg2.error;
if (this._pendingRequests[id]) {
this._pendingRequests[id](error, value);
delete this._pendingRequests[id];
}
}
}
}]);
return RemixExtension;
}();
if (window) window.RemixExtension = RemixExtension;
if (module && module.exports) module.exports = RemixExtension;
},{}]},{},[1]);
'use strict'
class RemixExtension {
constructor () {
this._notifications = {}
this._pendingRequests = {}
this._id = 0
window.addEventListener('message', (event) => this._newMessage(event), false)
}
listen (key, type, callback) {
if (!this._notifications[key]) this._notifications[key] = {}
this._notifications[key][type] = callback
}
call (key, type, params, callback) {
this._id++
this._pendingRequests[this._id] = callback
window.parent.postMessage(JSON.stringify({
action: 'request',
key,
type,
value: params,
id: this._id
}), '*')
}
_newMessage (event) {
if (!event.data) return
if (typeof event.data !== 'string') return
var msg
try {
msg = JSON.parse(event.data)
} catch (e) {
return console.log('unable to parse data')
}
const {action, key, type, value} = msg
if (action === 'notification') {
if (this._notifications[key] && this._notifications[key][type]) {
this._notifications[key][type](value)
}
} else if (action === 'response') {
const {id, error} = msg
if (this._pendingRequests[id]) {
this._pendingRequests[id](error, value)
delete this._pendingRequests[id]
}
}
}
}
if (window) window.RemixExtension = RemixExtension
if (module && module.exports) module.exports = RemixExtension
{
"name": "remix-extension",
"version": "0.0.1",
"description": "Ethereum IDE and tools for the web",
"contributors": [
{
"name": "Yann Levreau",
"email": "yann@ethdev.com"
}
],
"main": "./index.js",
"dependencies": {
"babel-eslint": "^7.1.1",
"babel-plugin-transform-object-assign": "^6.22.0",
"babel-preset-es2015": "^6.24.0",
"babelify": "^7.3.0",
"standard": "^7.0.1",
"tape": "^4.6.0"
},
"scripts": {
"browserify": "browserify index.js -o bundle.js"
},
"standard": {
"ignore": [
"node_modules/*"
],
"parser": "babel-eslint"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ethereum/remix-ide.git"
},
"author": "cpp-ethereum team",
"license": "MIT",
"bugs": {
"url": "https://github.com/ethereum/remix-ide/issues"
},
"homepage": "https://github.com/ethereum/remix-ide#readme",
"browserify": {
"transform": [
[
"babelify",
{
"plugins": [
[
"fast-async",
{
"runtimePatten": null,
"compiler": {
"promises": true,
"es7": true,
"noRuntime": true,
"wrapAwait": true
}
}
],
"transform-object-assign"
]
}
],
[
"babelify",
{
"presets": [
"es2015"
]
}
]
]
}
}
plugin api
# current APIs:
## 1) notifications
### app (key: app)
- unfocus `[]`
- focus `[]`
### compiler (key: compiler)
- compilationFinished `[success (bool), data (obj), source (obj)]`
- compilationData `[compilationResult]`
### transaction listener (key: txlistener)
- newTransaction `tx (obj)`
## 2) interactions
### app
- getExecutionContextProvider `@return {String} provider (injected | web3 | vm)`
- getProviderEndpoint `@return {String} url`
- updateTitle `@param {String} title`
- detectNetWork `@return {Object} {name, id}`
- addProvider `@param {String} name, @param {String} url`
- removeProvider `@return {String} name`
### config
- setConfig `@param {String} path, @param {String} content`
- getConfig `@param {String} path`
- removeConfig `@param {String} path`
### compiler
- getCompilationResult `@return {Object} compilation result`
### udapp (only VM)
- runTx `@param {Object} tx`
- getAccounts `@return {Array} acccounts`
- createVMAccount `@param {String} privateKey, @param {String} balance (hex)`
### editor
- getFilesFromPath `@param {Array} [path]`
- getCurrentFile `@return {String} path`
- getFile `@param {String} path`
- setFile `@param {String} path, @param {String} content`
- highlight `@param {Object} lineColumnPos {start: {line, column}, end: {line, column}}, @param {String} path, @param {String} hexColor`
- discardHighlight
'use strict'
var executionContext = require('../../execution-context')
var SourceHighlighter = require('../editor/sourceHighlighter')
/*
Defines available API. `key` / `type`
*/
module.exports = (pluginManager, fileProviders, fileManager, compilesrArtefacts, udapp) => {
let highlighters = {}
return {
app: {
getExecutionContextProvider: (mod, cb) => {
cb(null, executionContext.getProvider())
},
getProviderEndpoint: (mod, cb) => {
if (executionContext.getProvider() === 'web3') {
cb(null, executionContext.web3().currentProvider.host)
} else {
cb('no endpoint: current provider is either injected or vm')
}
},
updateTitle: (mod, title, cb) => {
pluginManager.plugins[mod].modal.setTitle(title)
if (cb) cb()
},
detectNetWork: (mod, cb) => {
executionContext.detectNetwork((error, network) => {
cb(error, network)
})
},
addProvider: (mod, name, url, cb) => {
executionContext.addProvider({ name, url })
cb()
},
removeProvider: (mod, name, cb) => {
executionContext.removeProvider(name)
cb()
}
},
config: {
setConfig: (mod, path, content, cb) => {
fileProviders['config'].set(mod + '/' + path, content)
cb()
},
getConfig: (mod, path, cb) => {
cb(null, fileProviders['config'].get(mod + '/' + path))
},
removeConfig: (mod, path, cb) => {
cb(null, fileProviders['config'].remove(mod + '/' + path))
if (cb) cb()
}
},
compiler: {
getCompilationResult: (mod, cb) => {
cb(null, compilesrArtefacts['__last'])
},
sendCompilationResult: (mod, file, source, languageVersion, data, cb) => {
pluginManager.receivedDataFrom('sendCompilationResult', mod, [file, source, languageVersion, data])
}
},
udapp: {
runTx: (mod, tx, cb) => {
executionContext.detectNetwork((error, network) => {
if (error) return cb(error)
if (network.name === 'Main' && network.id === '1') {
return cb('It is not allowed to make this action against mainnet')
}
udapp.silentRunTx(tx, (error, result) => {
if (error) return cb(error)
cb(null, {
transactionHash: result.transactionHash,
status: result.result.status,
gasUsed: '0x' + result.result.gasUsed.toString('hex'),
error: result.result.vm.exceptionError,
return: result.result.vm.return ? '0x' + result.result.vm.return.toString('hex') : '0x',
createdAddress: result.result.createdAddress ? '0x' + result.result.createdAddress.toString('hex') : undefined
})
})
})
},
getAccounts: (mod, cb) => {
executionContext.detectNetwork((error, network) => {
if (error) return cb(error)
if (network.name === 'Main' && network.id === '1') {
return cb('It is not allowed to make this action against mainnet')
}
udapp.getAccounts(cb)
})
},
createVMAccount: (mod, privateKey, balance, cb) => {
if (executionContext.getProvider() !== 'vm') return cb('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed')
udapp.createVMAccount(privateKey, balance, (error, address) => {
cb(error, address)
})
}
},
editor: {
getFilesFromPath: (mod, path, cb) => {
fileManager.filesFromPath(path, cb)
},
getCurrentFile: (mod, cb) => {
var path = fileManager.currentFile()
if (!path) {
cb('no file selected')
} else {
cb(null, path)
}
},
getFile: (mod, path, cb) => {
var provider = fileManager.fileProviderOf(path)
if (provider) {
// TODO add approval to user for external plugin to get the content of the given `path`
provider.get(path, (error, content) => {
cb(error, content)
})
} else {
cb(path + ' not available')
}
},
setFile: (mod, path, content, cb) => {
var provider = fileManager.fileProviderOf(path)
if (provider) {
// TODO add approval to user for external plugin to set the content of the given `path`
provider.set(path, content, (error) => {
if (error) return cb(error)
fileManager.syncEditor(path)
cb()
})
} else {
cb(path + ' not available')
}
},
highlight: (mod, lineColumnPos, filePath, hexColor, cb) => {
var position
try {
position = JSON.parse(lineColumnPos)
} catch (e) {
return cb(e.message)
}
if (!highlighters[mod]) highlighters[mod] = new SourceHighlighter()
highlighters[mod].currentSourceLocation(null)
highlighters[mod].currentSourceLocationFromfileName(position, filePath, hexColor)
cb()
},
discardHighlight: (mod, cb) => {
if (highlighters[mod]) highlighters[mod].currentSourceLocation(null)
cb()
}
}
}
}
'use strict'
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
const PluginAPI = require('./pluginAPI')
/**
* Register and Manage plugin:
*
* Plugin registration is done in the settings tab,
* using the following format:
* {
* "title": "<plugin name>",
* "url": "<plugin url>"
* }
*
* structure of messages:
*
* - Notification sent by Remix:
*{
* action: 'notification',
* key: <string>,
* type: <string>,
* value: <array>
*}
*
* - Request sent by the plugin:
*{
* id: <number>,
* action: 'request',
* key: <string>,
* type: <string>,
* value: <array>
*}
*
* - Response sent by Remix and receive by the plugin:
*{
* id: <number>,
* action: 'response',
* key: <string>,
* type: <string>,
* value: <array>,
* error: (see below)
*}
* => The `error` property is `undefined` if no error happened.
* => In case of error (due to permission, system error, API error, etc...):
* error: { code, msg (optional), data (optional), stack (optional)
* => possible error code are still to be defined, but the generic one would be 500.
*
* Plugin receive 4 types of message:
* - focus (when he get focus)
* - unfocus (when he loose focus - is hidden)
* - compilationData (that is triggered just after a focus - and send the current compilation data or null)
* - compilationFinished (that is only sent to the plugin that has focus)
*
* Plugin can emit messages and receive response.
*
* CONFIG:
* - getConfig(filename). The data to send should be formatted like:
* {
* id: <requestid>,
* action: 'request',
* key: 'config',
* type: 'getConfig',
* value: ['filename.ext']
* }
* the plugin will reveice a response like:
* {
* id: <requestid>,
* action: 'response',
* key: 'config',
* type: 'getConfig',
* error,
* value: ['content of filename.ext']
* }
* same apply for the other call
* - setConfig(filename, content)
* - removeConfig
*
* See index.html and remix.js in test-browser folder for sample
*
*/
module.exports = class PluginManager {
constructor (app, compilersArtefacts, txlistener, fileProviders, fileManager, udapp) {
const self = this
self.event = new EventManager()
var pluginAPI = new PluginAPI(
this,
fileProviders,
fileManager,
compilersArtefacts,
udapp
)
self._components = { pluginAPI }
self.plugins = {}
self.origins = {}
self.inFocus
fileManager.event.register('currentFileChanged', (file, provider) => {
self.broadcast(JSON.stringify({
action: 'notification',
key: 'editor',
type: 'currentFileChanged',
value: [ file ]
}))
})
txlistener.event.register('newTransaction', (tx) => {
self.broadcast(JSON.stringify({
action: 'notification',
key: 'txlistener',
type: 'newTransaction',
value: [tx]
}))
})
window.addEventListener('message', (event) => {
if (event.type !== 'message') return
var extension = self.origins[event.origin]
if (!extension) return
function response (key, type, callid, error, result) {
self.postToOrigin(event.origin, JSON.stringify({
id: callid,
action: 'response',
key: key,
type: type,
error: error,
value: [ result ]
}))
}
var data = JSON.parse(event.data)
data.value.unshift(extension)
data.value.push((error, result) => {
response(data.key, data.type, data.id, error, result)
})
if (pluginAPI[data.key] && pluginAPI[data.key][data.type]) {
pluginAPI[data.key][data.type].apply({}, data.value)
} else {
response(data.key, data.type, data.id, `Endpoint ${data.key}/${data.type} not present`, null)
}
}, false)
}
unregister (desc) {
const self = this
self._components.pluginAPI.editor.discardHighlight(desc.title, () => {})
delete self.plugins[desc.title]
delete self.origins[desc.url]
}
register (desc, modal, content) {
const self = this
self.plugins[desc.title] = { content, modal, origin: desc.url }
self.origins[desc.url] = desc.title
}
broadcast (value) {
for (var plugin in this.plugins) {
this.post(plugin, value)
}
}
postToOrigin (origin, value) {
if (this.origins[origin]) {
this.post(this.origins[origin], value)
}
}
receivedDataFrom (methodName, mod, argumentsArray) {
// TODO check whether 'mod' as right to do that
console.log(argumentsArray)
this.event.trigger(methodName, argumentsArray) // forward to internal modules
this.broadcast(JSON.stringify({ // forward to plugins
action: 'notification',
key: mod,
type: methodName,
value: argumentsArray
}))
}
post (name, value) {
const self = this
if (self.plugins[name]) {
self.plugins[name].content.querySelector('iframe').contentWindow.postMessage(value, self.plugins[name].origin)
}
}
}
'use strict'
module.exports = {
'oraclize': {
url: 'https://remix-plugin.oraclize.it',
title: 'Oraclize'
},
'solium': {
url: 'https://two-water.surge.sh',
title: 'Solium'
},
'ethdoc': {
url: 'https://30400.swarm-gateways.net/bzz:/ethdoc.remixide.eth',
title: 'Ethdoc'
},
'openzeppelin snippet': {
url: 'https://left-edge.surge.sh',
title: 'Openzeppelin snippet'
},
'vyper': {
url: 'https://plugin.vyper.live',
title: 'Vyper'
},
'slither/mythril': {
url: 'http://jittery-space.surge.sh',
title: 'Slither/Mythril'
},
'pipeline': {
url: 'https://pipeline.pipeos.one',
title: 'Pipeline'
}
/*
'etherscan-general': {
url: 'http://127.0.0.1:8081',
title: 'Etherscan-general'
}
*/
}
......@@ -4,10 +4,6 @@ var yo = require('yo-yo')
var $ = require('jquery')
var remixLib = require('remix-lib')
var utils = remixLib.util
var styleGuide = require('../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = require('./styles/staticAnalysisView-styles')
var globlalRegistry = require('../../global/registry')
......@@ -34,6 +30,7 @@ function staticAnalysisView (localRegistry) {
self.lastCompilationResult = null
self.lastCompilationSource = null
$('#staticanalysisresult').empty()
if (languageVersion.indexOf('soljson') !== 0) return
self.lastCompilationResult = data
self.lastCompilationSource = source
if (self.view.querySelector('#autorunstaticanalysis').checked) {
......@@ -46,30 +43,34 @@ staticAnalysisView.prototype.render = function () {
var self = this
var view = yo`
<div class="${css.analysis}">
<div id="staticanalysismodules">
${this.modulesView}
</div>
<div class="${css.buttons}">
<button class="${css.buttonRun}" onclick="${function () { self.run() }}" >Run</button>
<label class="${css.label}" for="autorunstaticanalysis">
<input id="autorunstaticanalysis"
type="checkbox"
style="vertical-align:bottom"
checked="true"
>
Auto run
</label>
<label class="${css.label}" for="checkAllEntries">
<input id="checkAllEntries"
type="checkbox"
onclick="${function (event) { self.checkAll(event) }}"
style="vertical-align:bottom"
checked="true"
>
Check/Uncheck all
</label>
</div>
<div class="${css.result}" "id='staticanalysisresult'></div>
<div class="${css.buttonsInner}">
<button class="${css.buttonRun} btn btn-sm btn-primary" onclick="${function () { self.run() }}" >Run</button>
<label class="${css.label}" for="autorunstaticanalysis">
<input id="autorunstaticanalysis"
type="checkbox"
style="vertical-align:bottom"
checked="true"
>
Auto run
</label>
<label class="${css.label}" for="checkAllEntries">
<input id="checkAllEntries"
type="checkbox"
onclick="${function (event) { self.checkAll(event) }}"
style="vertical-align:bottom"
checked="true"
>
Check/Uncheck all
</label>
</div>
</div>
<div id="staticanalysismodules" class="list-group list-group-flush ${css.container}">
${this.modulesView}
</div>
<hr>
<div><h6>Results:</h6></div>
<div class="${css.result}" id='staticanalysisresult'></div>
</div>
`
if (!this.view) {
......@@ -97,8 +98,8 @@ staticAnalysisView.prototype.run = function () {
var selected = this.selectedModules()
var warningContainer = $('#staticanalysisresult')
warningContainer.empty()
if (this.lastCompilationResult) {
var self = this
var self = this
if (this.lastCompilationResult && selected.length) {
var warningCount = 0
this.runner.run(this.lastCompilationResult, selected, function (results) {
results.map(function (result, i) {
......@@ -119,19 +120,16 @@ staticAnalysisView.prototype.run = function () {
}
warningCount++
var msg = yo`<span>${location} ${item.warning} ${item.more ? yo`<span><br><a href="${item.more}" target="blank">more</a></span>` : yo`<span></span>`}</span>`
self._deps.renderer.error(msg, warningContainer, {type: 'staticAnalysisWarning', useSpan: true})
self._deps.renderer.error(msg, warningContainer, {type: 'staticAnalysisWarning alert alert-warning', useSpan: true})
})
})
if (warningContainer.html() === '') {
$('#righthand-panel #menu .staticanalysisView').css('color', '')
warningContainer.html('No warning to report')
} else {
$('#righthand-panel #menu .staticanalysisView').css('color', styles.colors.red)
}
self.event.trigger('staticAnaysisWarning', [warningCount])
})
} else {
warningContainer.html('No compiled AST available')
if (selected.length) {
warningContainer.html('No compiled AST available')
}
self.event.trigger('staticAnaysisWarning', [-1])
}
}
......@@ -176,7 +174,7 @@ staticAnalysisView.prototype.renderModules = function () {
</label>
`
})
return yo`<div class="${css.analysisModulesContainer}">
return yo`<div class="${css.analysisModulesContainer} list-group-item py-1">
<label class="${css.label}"><b>${category[0].categoryDisplayName}</b></label>
${entriesDom}
</div>`
......
var csjs = require('csjs-inject')
var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.analysis {
display: flex;
......@@ -10,27 +7,29 @@ var css = csjs`
}
.result {
margin-top: 1%;
max-height: 300px;
}
.buttons {
${styles.rightPanel.analysisTab.box_AnalysisContainer}
margin: 1rem 0;
}
.buttonsInner {
display: flex;
align-items: center;
justify-content: space-around;
}
.buttonRun {
${styles.rightPanel.analysisTab.button_Run_AnalysisTab}
margin-right: 1%;
}
.analysisModulesContainer {
${styles.rightPanel.analysisTab.box_AnalysisContainer}
margin-bottom: 1%;
line-height: 2em;
display: flex;
flex-direction: column;
}
.label {
display: flex;
align-items: center;
}
.container {
max-height: 300px;
overflow-y: auto;
}
`
module.exports = css
......@@ -3,21 +3,46 @@ var StaticAnalysis = require('../staticanalysis/staticAnalysisView')
var EventManager = require('../../lib/events')
var css = require('./styles/analysis-tab-styles')
class AnalysisTab {
import { BaseApi } from 'remix-plugin'
import { EventEmitter } from 'events'
const profile = {
name: 'solidityStaticAnalysis',
displayName: 'Solidity static analysis',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMjA0OCIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMjA0OCAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0yMDQ4IDE1MzZ2MTI4aC0yMDQ4di0xNTM2aDEyOHYxNDA4aDE5MjB6bS0xMjgtMTI0OHY0MzVxMCAyMS0xOS41IDI5LjV0LTM1LjUtNy41bC0xMjEtMTIxLTYzMyA2MzNxLTEwIDEwLTIzIDEwdC0yMy0xMGwtMjMzLTIzMy00MTYgNDE2LTE5Mi0xOTIgNTg1LTU4NXExMC0xMCAyMy0xMHQyMyAxMGwyMzMgMjMzIDQ2NC00NjQtMTIxLTEyMXEtMTYtMTYtNy41LTM1LjV0MjkuNS0xOS41aDQzNXExNCAwIDIzIDl0OSAyM3oiLz48L3N2Zz4=',
description: 'Checks the contract code for security vulnerabilities and bad practices.',
kind: 'analysis',
location: 'swapPanel'
}
class AnalysisTab extends BaseApi {
constructor (registry) {
super(profile)
this.event = new EventManager()
this.events = new EventEmitter()
this.registry = registry
}
render () {
var staticanalysis = new StaticAnalysis()
this.registry.put({api: staticanalysis, name: 'staticanalysis'})
if (!this.staticanalysis) this.staticanalysis = new StaticAnalysis()
this.staticanalysis.event.register('staticAnaysisWarning', (count) => {
if (count > 0) {
this.events.emit('statusChanged', {key: count, title: `${count} warning${count === 1 ? '' : 's'}`, type: 'warning'})
} else if (count === 0) {
this.events.emit('statusChanged', {key: 'succeed', title: 'no warning', type: 'success'})
} else {
// count ==-1 no compilation result
this.events.emit('statusChanged', {key: 'none'})
}
})
this.registry.put({api: this.staticanalysis, name: 'staticanalysis'})
if (this.el) return this.el
this.el = yo`<div class="${css.analysisTabView} "id="staticanalysisView">${staticanalysis.render()}</div>`
return this.el
return yo`<div class="${css.analysisTabView}" id="staticanalysisView">${this.staticanalysis.render()}</div>`
}
}
module.exports = AnalysisTab
This diff is collapsed.
const async = require('async')
const EventEmitter = require('events')
var remixTests = require('remix-tests')
var Compiler = require('remix-solidity').Compiler
var CompilerImport = require('../../compiler/compiler-imports')
// TODO: move this to the UI
const addTooltip = require('../../ui/tooltip')
class CompileTab {
constructor (queryParams, fileManager, editor, config, fileProviders) {
this.event = new EventEmitter()
this.queryParams = queryParams
this.compilerImport = new CompilerImport()
this.compiler = new Compiler((url, cb) => this.importFileCb(url, cb))
this.fileManager = fileManager
this.editor = editor
this.config = config
this.fileProviders = fileProviders
}
init () {
this.optimize = this.queryParams.get().optimize
this.optimize = this.optimize === 'true'
this.queryParams.update({ optimize: this.optimize })
this.compiler.setOptimize(this.optimize)
}
setOptimize (newOptimizeValue) {
this.optimize = newOptimizeValue
this.queryParams.update({ optimize: this.optimize })
this.compiler.setOptimize(this.optimize)
}
runCompiler () {
this.fileManager.saveCurrentFile()
this.editor.clearAnnotations()
var currentFile = this.config.get('currentFile')
if (!currentFile) return
if (!/\.sol/.exec(currentFile)) return
// only compile *.sol file.
var target = currentFile
var sources = {}
var provider = this.fileManager.fileProviderOf(currentFile)
if (!provider) return console.log('cannot compile ' + currentFile + '. Does not belong to any explorer')
provider.get(target, (error, content) => {
if (error) return console.log(error)
sources[target] = { content }
this.event.emit('startingCompilation')
setTimeout(() => {
// setTimeout fix the animation on chrome... (animation triggered by 'staringCompilation')
this.compiler.compile(sources, target)
}, 100)
})
}
importExternal (url, cb) {
this.compilerImport.import(url,
// TODO: move to an event that is generated, the UI shouldn't be here
(loadingMsg) => { addTooltip(loadingMsg) },
(error, content, cleanUrl, type, url) => {
if (error) return cb(error)
if (this.fileProviders[type]) {
this.fileProviders[type].addReadOnly(cleanUrl, content, url)
}
cb(null, content)
})
}
importFileCb (url, filecb) {
if (url.indexOf('/remix_tests.sol') !== -1) return filecb(null, remixTests.assertLibCode)
var provider = this.fileManager.fileProviderOf(url)
if (provider) {
if (provider.type === 'localhost' && !provider.isConnected()) {
return filecb(`file provider ${provider.type} not available while trying to resolve ${url}`)
}
return provider.exists(url, (error, exist) => {
if (error) return filecb(error)
if (exist) {
return provider.get(url, filecb)
}
this.importExternal(url, filecb)
})
}
if (this.compilerImport.isRelativeImport(url)) {
// try to resolve localhost modules (aka truffle imports)
var splitted = /([^/]+)\/(.*)$/g.exec(url)
return async.tryEach([
(cb) => { this.importFileCb('localhost/installed_contracts/' + url, cb) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.importFileCb('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2], cb) } },
(cb) => { this.importFileCb('localhost/node_modules/' + url, cb) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.importFileCb('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2], cb) } }],
(error, result) => { filecb(error, result) }
)
}
this.importExternal(url, filecb)
}
}
module.exports = CompileTab
This diff is collapsed.
......@@ -3,8 +3,23 @@ var css = require('./styles/debugger-tab-styles')
var DebuggerUI = require('../debugger/debuggerUI')
class DebuggerTab {
import { BaseApi } from 'remix-plugin'
const profile = {
name: 'debugger',
displayName: 'Debugger',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDk2MHEwIDI2LTE5IDQ1dC00NSAxOWgtMjI0cTAgMTcxLTY3IDI5MGwyMDggMjA5cTE5IDE5IDE5IDQ1dC0xOSA0NXEtMTggMTktNDUgMTl0LTQ1LTE5bC0xOTgtMTk3cS01IDUtMTUgMTN0LTQyIDI4LjUtNjUgMzYuNS04MiAyOS05NyAxM3YtODk2aC0xMjh2ODk2cS01MSAwLTEwMS41LTEzLjV0LTg3LTMzLTY2LTM5LTQzLjUtMzIuNWwtMTUtMTQtMTgzIDIwN3EtMjAgMjEtNDggMjEtMjQgMC00My0xNi0xOS0xOC0yMC41LTQ0LjV0MTUuNS00Ni41bDIwMi0yMjdxLTU4LTExNC01OC0yNzRoLTIyNHEtMjYgMC00NS0xOXQtMTktNDUgMTktNDUgNDUtMTloMjI0di0yOTRsLTE3My0xNzNxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5IDQ1IDE5bDE3MyAxNzNoODQ0bDE3My0xNzNxMTktMTkgNDUtMTl0NDUgMTkgMTkgNDUtMTkgNDVsLTE3MyAxNzN2Mjk0aDIyNHEyNiAwIDQ1IDE5dDE5IDQ1em0tNDgwLTU3NmgtNjQwcTAtMTMzIDkzLjUtMjI2LjV0MjI2LjUtOTMuNSAyMjYuNSA5My41IDkzLjUgMjI2LjV6Ii8+PC9zdmc+',
description: 'Debug transactions',
kind: 'debugging',
location: 'swapPanel'
}
class DebuggerTab extends BaseApi {
constructor () {
super(profile)
this.el = null
}
......
const executionContext = require('../../execution-context')
import { NetworkApi } from 'remix-plugin'
export const profile = {
name: 'network',
description: 'Manage the network (mainnet, ropsten, goerli...) and the provider (web3, vm, injected)'
}
// Network API has :
// - events: ['providerChanged']
// - methods: ['getNetworkProvider', 'getEndpoint', 'detectNetwork', 'addNetwork', 'removeNetwork']
export class NetworkModule extends NetworkApi {
constructor () {
super(profile)
// TODO: See with remix-lib to make sementic coherent
executionContext.event.register('contextChanged', (provider) => {
this.events.emit('providerChanged', provider)
})
/*
// Events that could be implemented later
executionContext.event.register('removeProvider', (provider) => {
this.events.emit('networkRemoved', provider)
})
executionContext.event.register('addProvider', (provider) => {
this.events.emit('networkAdded', provider)
})
executionContext.event.register('web3EndpointChanged', (provider) => {
this.events.emit('web3EndpointChanged', provider)
})
*/
}
/** Return the current network provider (web3, vm, injected) */
getNetworkProvider () {
return executionContext.getProvider()
}
/** Return the current network */
detectNetwork () {
return new Promise((resolve, reject) => {
executionContext.detectNetwork((error, network) => {
error ? reject(error) : resolve(network)
})
})
}
/** Return the url only if network provider is 'web3' */
getEndpoint () {
const provider = executionContext.getProvider()
if (provider !== 'web3') {
throw new Error('no endpoint: current provider is either injected or vm')
}
return provider.web3().currentProvider.host
}
/** Add a custom network to the list of available networks */
addNetwork (customNetwork) {
executionContext.addProvider(customNetwork)
}
/** Remove a network to the list of availble networks */
removeNetwork (name) {
executionContext.removeProvider(name)
}
}
......@@ -12,24 +12,42 @@ var ContractDropdownUI = require('./runTab/contractDropdown.js')
var Recorder = require('./runTab/model/recorder.js')
var RecorderUI = require('./runTab/recorder.js')
class RunTab {
const executionContext = require('../../execution-context')
import { BaseApi } from 'remix-plugin'
const profile = {
name: 'run',
displayName: 'Deploy and run transactions',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzFfY29weSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4Ig0KCSB5PSIwcHgiIHdpZHRoPSI3NDIuNTQ1cHgiIGhlaWdodD0iNjc2Ljg4NnB4IiB2aWV3Qm94PSIwIC0wLjIwNCA3NDIuNTQ1IDY3Ni44ODYiDQoJIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAtMC4yMDQgNzQyLjU0NSA2NzYuODg2IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxwb2x5Z29uIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBwb2ludHM9IjI5NS45MTEsMC43MTEgNDg4LjkxMSwzMDQuMTg2IDQ4OC45MTEsMzk3LjE4MSAyOTMuOTExLDY3Ni41NTYgDQoJCTc0MS43ODYsMzQ5Ljk0MyAJIi8+DQoJPHBvbHlnb24gc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHBvaW50cz0iNDE3LjA4Myw0MDYuNTg5IDIwOS43OTEsNTE5LjQ5NCAxLjg0Niw0MDYuMjM0IDIwOS43OTEsNjc1Ljg2MyAJIi8+DQoJPHBvbHlnb24gc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHBvaW50cz0iNDE3LjA4MywzMTguNzA3IDIwOS43OTEsMC43MTEgMS44NDYsMzE4LjQyOCAyMDkuNzkxLDQzMS42ODkgCSIvPg0KPC9nPg0KPC9zdmc+DQo=',
description: 'execute and save transactions',
kind: 'run',
location: 'swapPanel'
}
class RunTab extends BaseApi {
constructor (udapp, udappUI, config, fileManager, editor, logCallback, filePanel, pluginManager, compilersArtefacts) {
super(profile)
this.event = new EventManager()
this.renderInstanceContainer()
this.renderSettings(udapp)
this.renderDropdown(udappUI, fileManager, pluginManager, compilersArtefacts, config, editor, udapp, filePanel, logCallback)
this.renderRecorder(udapp, udappUI, fileManager, config, logCallback)
this.renderRecorderCard()
this.renderContainer()
this.config = config
this.udapp = udapp
this.udappUI = udappUI
this.fileManager = fileManager
this.editor = editor
this.logCallback = logCallback
this.filePanel = filePanel
this.pluginManager = pluginManager
this.compilersArtefacts = compilersArtefacts
}
renderContainer () {
this.container = yo`<div class="${css.runTabView}" id="runTabView" ></div>`
var el = yo`
<div>
<div class="list-group list-group-flush">
${this.settingsUI.render()}
${this.contractDropdownUI.render()}
${this.recorderCard.render()}
......@@ -37,6 +55,7 @@ class RunTab {
</div>
`
this.container.appendChild(el)
return this.container
}
renderInstanceContainer () {
......@@ -46,7 +65,7 @@ class RunTab {
<div class=${css.instanceContainerTitle}
title="Autogenerated generic user interfaces for interaction with deployed contracts">
Deployed Contracts
<i class="${css.clearinstance} ${css.icon} fa fa-trash" onclick=${() => this.event.trigger('clearInstance', [])}
<i class="${css.clearinstance} ${css.icon} far fa-trash-alt" onclick=${() => this.event.trigger('clearInstance', [])}
title="Clear instances list and reset recorder" aria-hidden="true">
</i>
</div>`
......@@ -114,7 +133,7 @@ class RunTab {
renderRecorderCard () {
const collapsedView = yo`
<div class=${css.recorderCollapsedView}>
<div class=${css.recorderCount}>${this.recorderCount}</div>
<div class="${css.recorderCount} badge badge-pill badge-primary">${this.recorderCount}</div>
</div>`
const expandedView = yo`
......@@ -144,8 +163,18 @@ class RunTab {
}
render () {
return this.container
executionContext.init(this.config)
executionContext.stopListenOnLastBlock()
executionContext.listenOnLastBlock()
this.udapp.resetEnvironment()
this.renderInstanceContainer()
this.renderSettings(this.udapp)
this.renderDropdown(this.udappUI, this.fileManager, this.pluginManager, this.compilersArtefacts, this.config, this.editor, this.udapp, this.filePanel, this.logCallback)
this.renderRecorder(this.udapp, this.udappUI, this.fileManager, this.config, this.logCallback)
this.renderRecorderCard()
return this.renderContainer()
}
}
module.exports = RunTab
......@@ -18,6 +18,7 @@ class ContractDropdownUI {
listenToEvents () {
this.dropdownLogic.event.register('newlyCompiled', (success, data, source, compiler, compilerFullName) => {
if (!document.querySelector(`.${css.contractNames.classNames[0]}`)) return
var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`)
contractNames.innerHTML = ''
if (success) {
......@@ -43,11 +44,11 @@ class ContractDropdownUI {
}
render () {
this.compFails = yo`<i title="Contract compilation failed. Please check the compile tab for more information." class="fa fa-times-circle ${css.errorIcon}" ></i>`
var info = yo`<i class="fa fa-info ${css.infoDeployAction}" aria-hidden="true" title="*.sol files allows deploying and accessing contracts. *.abi files only allows accessing contracts."></i>`
this.compFails = yo`<i title="No contract compiled yet or compilation failed. Please check the compile tab for more information." class="fas fa-times-circle ${css.errorIcon}" ></i>`
var info = yo`<i class="fas fa-info ${css.infoDeployAction}" aria-hidden="true" title="*.sol files allows deploying and accessing contracts. *.abi files only allows accessing contracts."></i>`
this.atAddressButtonInput = yo`<input class="${css.input} ataddressinput" placeholder="Load contract from Address" title="atAddress" />`
this.selectContractNames = yo`<select class="${css.contractNames}" disabled></select>`
this.atAddressButtonInput = yo`<input class="${css.input} ${css.ataddressinput} ataddressinput form-control" placeholder="Load contract from Address" title="atAddress" />`
this.selectContractNames = yo`<select class="${css.contractNames} custom-select" disabled></select>`
this.createPanel = yo`<div class="${css.button}"></div>`
this.orLabel = yo`<div class="${css.orLabel}">or</div>`
......@@ -60,18 +61,19 @@ class ContractDropdownUI {
${this.createPanel}
${this.orLabel}
<div class="${css.button} ${css.atAddressSect}">
<div class="${css.atAddress}" onclick=${this.loadFromAddress.bind(this)}>At Address</div>
<div class="${css.atAddress} btn btn-sm btn-info" onclick=${this.loadFromAddress.bind(this)}>At Address</div>
${this.atAddressButtonInput}
</div>
</div>
</div>
`
this.selectContractNames.addEventListener('change', this.setInputParamsPlaceHolder.bind(this))
this.setInputParamsPlaceHolder()
return el
}
changeCurrentFile (currentFile) {
if (!document.querySelector(`.${css.contractNames}`)) return
document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError)
var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`)
contractNames.innerHTML = ''
......@@ -139,7 +141,7 @@ class ContractDropdownUI {
}
var promptCb = (okCb, cancelCb) => {
modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
modalDialogCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
}
var statusCb = (msg) => {
......@@ -153,7 +155,7 @@ class ContractDropdownUI {
return this.logCallback(error)
}
this.event.trigger('newContractInstanceAdded', [contractObject, address, this.selectContractNames.value])
this.event.trigger('newContractInstanceAdded', [contractObject, address, contractObject.name])
}
if (selectedContract.isOverSizeLimit()) {
......@@ -163,7 +165,7 @@ class ContractDropdownUI {
{
label: 'Force Send',
fn: () => {
this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialogCustom, confirmDialog, statusCb, finalCb)
this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, finalCb)
}}, {
label: 'Cancel',
fn: () => {
......@@ -171,7 +173,7 @@ class ContractDropdownUI {
}
})
}
this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialogCustom, confirmDialog, statusCb, finalCb)
this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, finalCb)
}
loadFromAddress () {
......@@ -180,7 +182,7 @@ class ContractDropdownUI {
var address = this.atAddressButtonInput.value
this.dropdownLogic.loadContractFromAddress(address,
(cb) => {
modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition ?', cb)
modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition?', cb)
},
(error, loadType, abi) => {
if (error) {
......
......@@ -21,7 +21,7 @@ class DropdownLogic {
this.listenToCompilationEvents()
fileManager.event.register('currentFileChanged', (currentFile) => {
fileManager.events.on('currentFileChanged', (currentFile) => {
this.event.trigger('currentFileChanged', [currentFile])
})
}
......@@ -121,7 +121,7 @@ class DropdownLogic {
}
// TODO: check if selectedContract and data can be joined
createContract (selectedContract, data, continueCb, promptCb, confirmDialog, modalDialog, finalCb) {
createContract (selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, finalCb) {
if (data) {
data.contractName = selectedContract.name
data.linkReferences = selectedContract.bytecodeLinkReferences
......
......@@ -23,9 +23,9 @@ class RecorderUI {
.recorder {}
`
this.runButton = yo`<i class="fa fa-play runtransaction ${css2.runTxs} ${css.icon}" title="Run Transactions" aria-hidden="true"></i>`
this.runButton = yo`<i class="fas fa-play runtransaction ${css2.runTxs} ${css.icon}" title="Run Transactions" aria-hidden="true"></i>`
this.recordButton = yo`
<i class="fa fa-floppy-o savetransaction ${css2.recorder} ${css.icon}"
<i class="fas fa-save savetransaction ${css2.recorder} ${css.icon}"
onclick=${this.triggerRecordButton.bind(this)} title="Save Transactions" aria-hidden="true">
</i>`
......@@ -56,7 +56,7 @@ class RecorderUI {
}
var promptCb = (okCb, cancelCb) => {
modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
modalDialogCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
}
var alertCb = (msg) => {
......@@ -76,7 +76,7 @@ class RecorderUI {
triggerRecordButton () {
this.recorder.saveScenario(
(path, cb) => {
modalDialogCustom.prompt(null, 'Transactions will be saved in a file under ' + path, 'scenario.json', cb)
modalDialogCustom.prompt('Save transactions as scenario', 'Transactions will be saved in a file under ' + path, 'scenario.json', cb)
},
(error) => {
if (error) return modalDialogCustom.alert(error)
......
var $ = require('jquery')
var yo = require('yo-yo')
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var css = require('../styles/run-tab-styles')
var copyToClipboard = require('../../ui/copy-to-clipboard')
var modalDialogCustom = require('../../ui/modal-dialog-custom')
var addTooltip = require('../../ui/tooltip')
var helper = require('../../../lib/helper.js')
const $ = require('jquery')
const yo = require('yo-yo')
const remixLib = require('remix-lib')
const EventManager = remixLib.EventManager
const css = require('../styles/run-tab-styles')
const copyToClipboard = require('../../ui/copy-to-clipboard')
const modalDialogCustom = require('../../ui/modal-dialog-custom')
const addTooltip = require('../../ui/tooltip')
const helper = require('../../../lib/helper.js')
const globalRegistry = require('../../../global/registry')
class SettingsUI {
constructor (settings) {
this.settings = settings
this.event = new EventManager()
this._components = {}
this.settings.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => {
if (error) return
......@@ -20,6 +22,11 @@ class SettingsUI {
this.updateAccountBalances()
})
this._components.registry = globalRegistry
this._deps = {
networkModule: this._components.registry.get('network').api
}
setInterval(() => {
this.updateAccountBalances()
}, 10 * 1000)
......@@ -40,7 +47,7 @@ class SettingsUI {
}
render () {
this.netUI = yo`<span class=${css.network}></span>`
this.netUI = yo`<span class="${css.network} badge badge-secondary"></span>`
var environmentEl = yo`
<div class="${css.crow}">
......@@ -48,8 +55,7 @@ class SettingsUI {
Environment
</div>
<div class=${css.environment}>
${this.netUI}
<select id="selectExEnvOptions" onchange=${() => { this.updateNetwork() }} class="${css.select}">
<select id="selectExEnvOptions" onchange=${() => { this.updateNetwork() }} class="form-control ${css.select}">
<option id="vm-mode"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm" checked name="executionContext"> JavaScript VM
......@@ -64,48 +70,59 @@ class SettingsUI {
value="web3" name="executionContext"> Web3 Provider
</option>
</select>
<a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md" target="_blank"><i class="${css.icon} fa fa-info"></i></a>
<a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md" target="_blank"><i class="${css.infoDeployAction} fas fa-info"></i></a>
</div>
</div>
`
var accountEl = yo`
const networkEl = yo`
<div class="${css.crow}">
<div class="${css.col1_1}">
</div>
<div class="${css.environment}">
${this.netUI}
</div>
</div>
`
const accountEl = yo`
<div class="${css.crow}">
<div class="${css.col1_1}">
Account
<i class="fa fa-plus-circle ${css.icon}" aria-hidden="true" onclick=${this.newAccount.bind(this)} title="Create a new account"></i>
<i class="fas fa-plus-circle ${css.icon}" aria-hidden="true" onclick=${this.newAccount.bind(this)} title="Create a new account"></i>
</div>
<div class=${css.account}>
<select name="txorigin" class="${css.select}" id="txorigin"></select>
<select name="txorigin" class="form-control ${css.select}" id="txorigin"></select>
${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)}
<i class="fa fa-pencil-square-o ${css.icon}" aria-hiden="true" onclick=${this.signMessage.bind(this)} title="Sign a message using this account key"></i>
<i class="fas fa-edit ${css.icon}" aria-hiden="true" onclick=${this.signMessage.bind(this)} title="Sign a message using this account key"></i>
</div>
</div>
`
var gasPriceEl = yo`
const gasPriceEl = yo`
<div class="${css.crow}">
<div class="${css.col1_1}">Gas limit</div>
<input type="number" class="${css.col2}" id="gasLimit" value="3000000">
<input type="number" class="form-control ${css.gasNval} ${css.col2}" id="gasLimit" value="3000000">
</div>
`
var valueEl = yo`
const valueEl = yo`
<div class="${css.crow}">
<div class="${css.col1_1}">Value</div>
<input type="text" class="${css.col2_1}" id="value" value="0" title="Enter the value and choose the unit">
<select name="unit" class="${css.col2_2}" id="unit">
<option data-unit="wei">wei</option>
<option data-unit="gwei">gwei</option>
<option data-unit="finney">finney</option>
<option data-unit="ether">ether</option>
</select>
<div class="${css.gasValueContainer}">
<input type="text" class="form-control ${css.gasNval} ${css.col2}" id="value" value="0" title="Enter the value and choose the unit">
<select name="unit" class="form-control ${css.gasNvalUnit} ${css.col2_2}" id="unit">
<option data-unit="wei">wei</option>
<option data-unit="gwei">gwei</option>
<option data-unit="finney">finney</option>
<option data-unit="ether">ether</option>
</select>
</div>
</div>
`
var el = yo`
const el = yo`
<div class="${css.settings}">
${environmentEl}
${networkEl}
${accountEl}
${gasPriceEl}
${valueEl}
......@@ -133,9 +150,12 @@ class SettingsUI {
this.settings.event.register('addProvider', (network) => {
selectExEnv.appendChild(yo`<option
title="Manually added environment: ${network.url}"
value="${network.name}" name="executionContext"> ${network.name}
</option>`)
title="Manually added environment: ${network.url}"
value="${network.name}"
name="executionContext"
>
${network.name}
</option>`)
addTooltip(`${network.name} [${network.url}] added`)
})
......@@ -150,8 +170,8 @@ class SettingsUI {
selectExEnv.addEventListener('change', (event) => {
let context = selectExEnv.options[selectExEnv.selectedIndex].value
this.settings.changeExecutionContext(context, () => {
modalDialogCustom.confirm(null, 'Are you sure you want to connect to an ethereum node?', () => {
modalDialogCustom.prompt(null, 'Web3 Provider Endpoint', 'http://localhost:8545', (target) => {
modalDialogCustom.confirm('External node request', 'Are you sure you want to connect to an ethereum node?', () => {
modalDialogCustom.prompt('External node request', 'Web3 Provider Endpoint', 'http://localhost:8545', (target) => {
this.settings.setProviderFromEndpoint(target, context, (alertMsg) => {
if (alertMsg) {
modalDialogCustom.alert(alertMsg)
......@@ -229,7 +249,8 @@ class SettingsUI {
this.netUI.innerHTML = 'can\'t detect network '
return
}
this.netUI.innerHTML = `<i class="${css.networkItem} fa fa-plug" aria-hidden="true"></i> ${name} (${id || '-'})`
let network = this._deps.networkModule.getNetworkProvider
this.netUI.innerHTML = (network() !== 'vm') ? `${name} (${id || '-'}) network` : ''
})
}
......
This diff is collapsed.
......@@ -4,8 +4,6 @@ const css = csjs`
.analysisTabView {
padding: 2%;
padding-bottom: 3em;
display: flex;
flex-direction: column;
}
`
......
const csjs = require('csjs-inject')
const css = csjs`
.compilerArticle {
padding: 10px;
}
.title {
font-size: 1.1em;
font-weight: bold;
margin-bottom: 1em;
}
.panicError {
color: red;
font-size: 20px;
}
.crow {
display: flex;
overflow: auto;
clear: both;
padding: .2em;
}
.checkboxText {
font-weight: normal;
}
.crow label {
cursor:pointer;
}
.crowNoFlex {
overflow: auto;
clear: both;
}
.info {
padding: 10px;
word-break: break-word;
}
.contract {
display: block;
margin: 3% 0;
}
.autocompileContainer {
display: flex;
align-items: center;
}
.hideWarningsContainer {
display: flex;
align-items: center;
}
.autocompile {}
.autocompileTitle {
font-weight: bold;
margin: 1% 0;
}
.autocompileText {
margin: 1% 0;
font-size: 12px;
overflow: hidden;
word-break: normal;
line-height: initial;
}
.warnCompilationSlow {
margin-left: 1%;
}
.compilerConfig {
display: flex;
align-items: center;
}
.compilerConfig label {
margin: 0;
}
.compilerSm {
padding-left: 1.25rem;
}
.name {
display: flex;
}
.size {
display: flex;
}
.checkboxes {
display: flex;
width: 100%;
justify-content: space-between;
flex-wrap: wrap;
}
.compileButton {
width: 100%;
margin: 15px 0 10px 0;
font-size: 12px;
}
.container {
margin: 0;
margin-bottom: 2%;
}
.optimizeContainer {
display: flex;
}
.noContractAlert {
display: flex;
justify-content: center;
align-items: center;
}
.contractHelperButtons {
margin-top: 10px;
display: flex;
align-items: center;
justify-content: space-between;
float: right;
}
.copyToClipboard {
font-size: 1rem;
}
.copyIcon {
margin-right: 5px;
}
.log {
display: flex;
flex-direction: column;
margin-bottom: 5%;
overflow: visible;
}
.key {
margin-right: 5px;
text-transform: uppercase;
width: 100%;
}
.value {
display: flex;
width: 100%;
margin-top: 1.5%;
}
.questionMark {
margin-left: 2%;
cursor: pointer;
}
.questionMark:hover {
}
.detailsJSON {
padding: 8px 0;
border: none;
}
.icon {
margin-right: 0.3em;
}
.errorBlobs {
padding-left: 5px;
padding-right: 5px;
}
.spinningIcon {
display: inline-block;
position: relative;
animation: spin 2s infinite linear;
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
-webkit-animation: spin 2s infinite linear;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-webkit-keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-moz-keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-o-keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-ms-keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.bouncingIcon {
display: inline-block;
position: relative;
-moz-animation: bounce 2s infinite linear;
-o-animation: bounce 2s infinite linear;
-webkit-animation: bounce 2s infinite linear;
animation: bounce 2s infinite linear;
}
@-webkit-keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}
@-moz-keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}
@-o-keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}
@-ms-keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}
@keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}
`
module.exports = css
var csjs = require('csjs-inject')
var styles = require('../../ui/styles-guide/theme-chooser').chooser()
const css = csjs`
.debuggerTabView {
......@@ -7,7 +6,6 @@ const css = csjs`
}
.debugger {
margin-bottom: 1%;
${styles.rightPanel.debuggerTab.box_Debugger}
}
`
......
var csjs = require('csjs-inject')
var styleGuide = require('../../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var css = csjs`
.runTabView {
......@@ -14,28 +12,25 @@ var css = csjs`
font-size: 12px;
display: flex;
justify-content: space-between;
padding-left: 15px;
}
.settings {
${styles.rightPanel.runTab.box_RunTab}
margin-bottom: 2%;
padding: 10px 15px 15px 15px;
padding: 10px 0px 15px 15px;
}
.recorderCount {
border: 1px solid ${styles.rightPanel.runTab.icon_HoverColor};
border-radius: 50%;
margin-right: 30px;
min-width: 13px;
height: 13px;
display: flex;
justify-content: center;
align-items: center;
font-size: 10px;
/* margin-right: 30px; */
/* min-width: 13px; */
/* display: flex; */
/* justify-content: center; */
/* align-items: center; */
/* font-size: 10px; */
}
.crow {
margin-top: .5em;
display: flex;
align-items: center;
width: 500px;
/*width: 500px;*/
}
.col1 {
width: 30%;
......@@ -44,7 +39,6 @@ var css = csjs`
}
.col1_1 {
font-size: 12px;
width: 15%;
min-width: 75px;
float: left;
align-self: center;
......@@ -53,43 +47,36 @@ var css = csjs`
display: flex;
align-items: center;
position: relative;
width: 259px;
width: 100%;
padding-right: 25px;
}
.account {
display: flex;
align-items: center;
width: 266px;
width: 90%;
}
.col2 {
${styles.rightPanel.runTab.input_RunTab}
border-radius: 3px;
}
.col2_1 {
${styles.rightPanel.runTab.input_RunTab}
width: 164px;
min-width: 164px;
}
.col2_2 {
${styles.rightPanel.runTab.dropdown_RunTab}
width: 82px;
min-width: 82px;
}
.select {
${styles.rightPanel.runTab.dropdown_RunTab}
font-weight: normal;
width: 250px;
width: 100%;
}
.instanceContainer {
${styles.rightPanel.runTab.box_Instance}
display: flex;
flex-direction: column;
margin-bottom: 2%;
border: none;
text-align: center;
padding: 10px 0px 15px 15px;
padding: 10px 0px 15px 0px;
}
.pendingTxsContainer {
${styles.rightPanel.runTab.box_Instance}
display: flex;
flex-direction: column;
margin-top: 2%;
......@@ -97,8 +84,8 @@ var css = csjs`
text-align: center;
}
.container {
${styles.rightPanel.runTab.box_RunTab}
margin-bottom: 2%;
margin-bottom: 4%;
padding-left: 15px;
}
.recorderCollapsedView,
.recorderExpandedView {
......@@ -109,12 +96,10 @@ var css = csjs`
margin: 0 15px 15px 0;
}
.contractNames {
${styles.rightPanel.runTab.dropdown_RunTab}
width: 100%;
border: 1px solid
}
.contractNamesError {
border: 1px solid ${styles.appProperties.errorText_Color}
}
.subcontainer {
display: flex;
......@@ -127,18 +112,16 @@ var css = csjs`
margin-top: 13px;
}
.transaction {
${styles.rightPanel.runTab.button_transaction}
}
.atAddress {
margin: 0;
min-width: 100px;
width: 100px;
font-size: 10px;
/* font-size: 10px; */
word-break: inherit;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 0;
${styles.rightPanel.runTab.button_atAddress}
}
.atAddressSect {
margin-top: 6px;
......@@ -147,20 +130,20 @@ var css = csjs`
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.ataddressinput {
padding: .25rem;
}
.create {
${styles.rightPanel.runTab.button_Create}
}
.input {
${styles.rightPanel.runTab.input_RunTab};
font-size: 10px;
}
.noInstancesText {
${styles.rightPanel.runTab.box_Instance}
font-style: italic;
text-align: left;
padding-left: 15px;
}
.pendingTxsText {
${styles.rightPanel.runTab.borderBox_Instance}
font-style: italic;
display: flex;
justify-content: space-evenly;
......@@ -173,15 +156,15 @@ var css = csjs`
align-items: center;
}
.transact {
color: ${styles.colors.lightRed};
color: var(--warning);
margin-right: .3em;
}
.payable {
color: ${styles.colors.red};
color: var(--warning);
margin-right: .3em;
}
.call {
color: ${styles.colors.lightBlue};
color: var(--info);
margin-right: .3em;
}
.pendingContainer {
......@@ -199,31 +182,23 @@ var css = csjs`
cursor: pointer;
font-size: 12px;
cursor: pointer;
color: ${styles.rightPanel.runTab.icon_Color};
margin-left: 5px;
}
.icon:hover {
font-size: 12px;
color: ${styles.rightPanel.runTab.icon_HoverColor};
color: var(--warning);
}
.errorIcon {
color: ${styles.appProperties.errorText_Color};
color: var(--warning);
margin-left: 15px;
}
.failDesc {
color: ${styles.appProperties.errorText_Color};
color: var(--warning);
padding-left: 10px;
display: inline;
}
.network {
display: flex;
justify-content: flex-end;
align-items: center;
position: absolute;
color: grey;
width: 100%;
height: 100%;
padding-right: 28px;
margin-left: 8px;
pointer-events: none;
}
.networkItem {
......@@ -235,7 +210,6 @@ var css = csjs`
.transactionActions {
display: flex;
justify-content: space-evenly;
${styles.rightPanel.runTab.box_Info_RunTab};
width: 145px;
}
.orLabel {
......@@ -244,6 +218,24 @@ var css = csjs`
.infoDeployAction {
margin-left: 5px;
font-size: 13px;
color: var(--info);
}
.gasValueContainer {
flex-direction: row;
display: flex;
}
.gasNval {
/* transform: scale(0.7); */
/* transform-origin: left; */
margin-right: 10px;
width: 100px;
font-size: 0.8rem;
}
.gasNvalUnit {
/* transform: scale(0.7); */
/* transform-origin: left; */
margin-right: 10px;
font-size: 0.8rem;
}
`
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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