Merge branch 'swap_it' of https://github.com/ethereum/remix-ide into swap_it

pull/3094/head
Rob Stupay 6 years ago
commit 8614383ea5
  1. 43
      src/app.js
  2. 4
      src/app/components/vertical-icons-api.js
  3. 4
      src/app/staticanalysis/staticAnalysisView.js
  4. 556
      src/app/tabs/compile-tab.js
  5. 98
      src/app/tabs/compileTab/compileTab.js
  6. 227
      src/app/tabs/compileTab/compilerContainer.js
  7. 1
      src/app/tabs/runTab/contractDropdown.js
  8. 213
      src/app/tabs/styles/compile-tab-styles.js
  9. 56
      src/app/ui/landing-page/generate.js
  10. 57
      src/app/ui/landing-page/landing-page.js
  11. 61
      src/app/ui/landing-page/section.js
  12. 24
      src/framingService.js
  13. 8
      src/lib/panels-resize.js
  14. 79
      src/lib/store.js

@ -55,6 +55,8 @@ const FilePanel = require('./app/panels/file-panel')
import PanelsResize from './lib/panels-resize'
import { EntityStore } from './lib/store'
import { RemixAppManager } from './remixAppManager'
import { generateHomePage, homepageProfile } from './app/ui/landing-page/generate'
import framingService from './framingService'
var styleGuide = require('./app/ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()
@ -412,10 +414,10 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
const pluginManagerComponent = new PluginManagerComponent()
const swapPanelComponent = new SwapPanelComponent()
const mainPanelComponent = new SwapPanelComponent()
const verticalIconComponent = new VerticalIconsComponent()
const swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconComponent) // eslint-disable-line
const mainPanelApi = new SwapPanelApi(mainPanelComponent, verticalIconComponent) // eslint-disable-line
const verticalIconsApi = new VerticalIconsApi(verticalIconComponent) // eslint-disable-line
const verticalIconsComponent = new VerticalIconsComponent()
const swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconsComponent) // eslint-disable-line
const mainPanelApi = new SwapPanelApi(mainPanelComponent, verticalIconsComponent) // eslint-disable-line
const verticalIconsApi = new VerticalIconsApi(verticalIconsComponent) // eslint-disable-line
let appStore = new EntityStore('module', { actives: [], ids: [], entities: {} })
const appManager = new RemixAppManager(appStore, swapPanelApi, mainPanelApi, verticalIconsApi)
@ -428,12 +430,12 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
self._components.fileManager.init()
self._view.mainpanel.appendChild(mainPanelComponent.render())
self._view.iconpanel.appendChild(verticalIconComponent.render())
self._view.iconpanel.appendChild(verticalIconsComponent.render())
self._view.swappanel.appendChild(swapPanelComponent.render())
let filePanel = new FilePanel()
registry.put({api: filePanel, name: 'filepanel'})
let compileTab = new CompileTab(self._components.registry)
let compileTab = new CompileTab(registry)
let run = new RunTab(
registry.get('udapp').api,
registry.get('udappUI').api,
@ -446,14 +448,15 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
registry.get('compilersartefacts').api
)
let settings = new SettingsTab(self._components.registry)
let analysis = new AnalysisTab(self._components.registry)
let debug = new DebuggerTab(self._components.registry)
let support = new SupportTab(self._components.registry)
let analysis = new AnalysisTab(registry)
let debug = new DebuggerTab()
let support = new SupportTab()
let test = new TestTab(self._components.registry, compileTab)
let sourceHighlighters = registry.get('editor').api.sourceHighlighters
let configProvider = self._components.filesProviders['config']
appManager.init([
{ profile: homepageProfile(), api: generateHomePage() },
{ profile: this.profile(), api: this },
{ profile: udapp.profile(), api: udapp },
{ profile: fileManager.profile(), api: fileManager },
@ -475,26 +478,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
])
appManager.registerMany(appManager.plugins())
swapPanelApi.event.on('toggle', () => {
this._components.resizeFeature.panel1.clientWidth !== 0 ? this._components.resizeFeature.minimize() : this._components.resizeFeature.maximise()
})
swapPanelApi.event.on('showing', (moduleName) => {
this._components.resizeFeature.panel1.clientWidth === 0 ? this._components.resizeFeature.maximise() : ''
var current = appStore.getOne(moduleName)
// warn the content that it is being displayed. TODO should probably be done in each view
if (current && current.api.__showing) current.api.__showing()
})
mainPanelApi.event.on('showing', (moduleName) => {
if (moduleName === 'code editor') {
verticalIconComponent.select('file explorers')
this._components.resizeFeature.maximise()
return
}
this._components.resizeFeature.minimize()
})
framingService.start(appStore, swapPanelApi, verticalIconsApi, mainPanelApi, this._components.resizeFeature)
verticalIconComponent.select('file explorers')
verticalIconComponent.select('code editor')
// The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event.
var filesToLoad = null
@ -516,7 +501,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
self.loadFiles(filesToLoad)
}
var txLogger = new TxLogger() // eslint-disable-line
var txLogger = new TxLogger() // eslint-disable-line
txLogger.event.register('debuggingRequested', (hash) => { debug.debugger().debug(hash) })
let transactionContextAPI = {

@ -12,5 +12,9 @@ class VerticalIconsApi {
removeIcon (mod) {
this.component.removeIcon(mod)
}
select (moduleName) {
this.component.select(moduleName)
}
}
module.exports = VerticalIconsApi

@ -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')

@ -1,130 +1,72 @@
/* global Worker */
/* global */
const EventEmitter = require('events')
const async = require('async')
const $ = require('jquery')
const yo = require('yo-yo')
const csjs = require('csjs-inject')
const copy = require('clipboard-copy')
var minixhr = require('minixhr')
var remixTests = require('remix-tests')
var Compiler = require('remix-solidity').Compiler
var CompilerImport = require('../compiler/compiler-imports')
var QueryParams = require('../../lib/query-params')
var globalRegistry = require('../../global/registry')
const TreeView = require('../ui/TreeView')
const modalDialog = require('../ui/modaldialog')
const copyToClipboard = require('../ui/copy-to-clipboard')
const modalDialogCustom = require('../ui/modal-dialog-custom')
const styleGuide = require('../ui/styles-guide/theme-chooser')
const parseContracts = require('../contract/contractParser')
const publishOnSwarm = require('../contract/publishOnSwarm')
const addTooltip = require('../ui/tooltip')
var helper = require('../../lib/helper')
const styles = styleGuide.chooser()
var css = require('./styles/compile-tab-styles')
module.exports = class CompileTab {
constructor (localRegistry) {
const CompileTabLogic = require('./compileTab/compileTab.js')
const CompilerContainer = require('./compileTab/compilerContainer.js')
class CompileTab {
constructor (registry) {
const self = this
self.event = new EventEmitter()
self._view = {
el: null,
autoCompile: null,
compileButton: null,
warnCompilationSlow: null,
compileIcon: null,
compileContainer: null,
errorContainer: null,
errorContainerHead: null,
contractNames: null,
contractEl: null,
config: {
solidity: null
},
optimize: null
contractEl: null
}
self._components = {}
self._components.registry = localRegistry || globalRegistry
self._components.queryParams = new QueryParams()
self._components.compilerImport = new CompilerImport(() => { return self._deps.config.get('settings/gist-access-token') })
self._components.compiler = new Compiler((url, cb) => self.importFileCb(url, cb))
self.queryParams = new QueryParams()
// dependencies
self._deps = {
editor: self._components.registry.get('editor').api,
config: self._components.registry.get('config').api,
renderer: self._components.registry.get('renderer').api,
swarmfileProvider: self._components.registry.get('fileproviders/swarm').api,
fileManager: self._components.registry.get('filemanager').api,
fileProviders: self._components.registry.get('fileproviders').api
editor: registry.get('editor').api,
config: registry.get('config').api,
renderer: registry.get('renderer').api,
swarmfileProvider: registry.get('fileproviders/swarm').api,
fileManager: registry.get('filemanager').api,
fileProviders: registry.get('fileproviders').api,
pluginManager: registry.get('pluginmanager').api
}
self.data = {
hideWarnings: self._deps.config.get('hideWarnings') || false,
autoCompile: self._deps.config.get('autoCompile'),
compileTimeout: null,
contractsDetails: {},
maxTime: 1000,
timeout: 300,
allversions: null,
selectedVersion: null,
defaultVersion: 'soljson-v0.5.1+commit.c8a2cb62.js', // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler
baseurl: 'https://solc-bin.ethereum.org/bin'
contractsDetails: {}
}
self.data.optimize = self._components.queryParams.get().optimize
self.data.optimize = self.data.optimize === 'true'
self._components.queryParams.update({ optimize: self.data.optimize })
self._components.compiler.setOptimize(self.data.optimize)
self._deps.editor.event.register('contentChanged', scheduleCompilation)
self._deps.editor.event.register('sessionSwitched', scheduleCompilation)
function scheduleCompilation () {
if (!self._deps.config.get('autoCompile')) return
if (self.data.compileTimeout) window.clearTimeout(self.data.compileTimeout)
self.data.compileTimeout = window.setTimeout(() => self.runCompiler(), self.data.timeout)
}
self._components.compiler.event.register('compilationDuration', function tabHighlighting (speed) {
if (!self._view.warnCompilationSlow) return
if (speed > self.data.maxTime) {
const msg = `Last compilation took ${speed}ms. We suggest to turn off autocompilation.`
self._view.warnCompilationSlow.setAttribute('title', msg)
self._view.warnCompilationSlow.style.visibility = 'visible'
} else {
self._view.warnCompilationSlow.style.visibility = 'hidden'
}
})
self._deps.editor.event.register('contentChanged', function changedFile () {
if (!self._view.compileIcon) return
self._view.compileIcon.classList.add(`${css.bouncingIcon}`) // @TODO: compileView tab
})
self._components.compiler.event.register('loadingCompiler', function start () {
if (!self._view.compileIcon) return
self._view.compileIcon.classList.add(`${css.spinningIcon}`)
self._view.warnCompilationSlow.style.visibility = 'hidden'
self._view.compileIcon.setAttribute('title', 'compiler is loading, please wait a few moments.')
})
self._components.compiler.event.register('compilationStarted', function start () {
if (!self._view.compileIcon) return
this.compileTabLogic = new CompileTabLogic(self.queryParams, self._deps.fileManager, self._deps.editor, self._deps.config, self._deps.fileProviders)
this.compiler = this.compileTabLogic.compiler
this.compileTabLogic.init()
this.compilerContainer = new CompilerContainer(self.compileTabLogic, self._deps.editor, self._deps.config, self.queryParams)
this.listenToEvents()
}
listenToEvents () {
const self = this
self.compiler.event.register('compilationStarted', () => {
self._view.errorContainer.innerHTML = ''
self._view.errorContainerHead.innerHTML = ''
self._view.compileIcon.classList.remove(`${css.bouncingIcon}`)
self._view.compileIcon.classList.add(`${css.spinningIcon}`)
self._view.compileIcon.setAttribute('title', 'compiling...')
})
self._components.compiler.event.register('compilerLoaded', function loaded () {
if (!self._view.compileIcon) return
self._view.compileIcon.classList.remove(`${css.spinningIcon}`)
self._view.compileIcon.setAttribute('title', '')
})
self._components.compiler.event.register('compilationFinished', function finish (success, data, source) {
self.compiler.event.register('compilationFinished', (success, data, source) => {
if (success) {
// forwarding the event to the appManager infra
self.event.emit('compilationFinished', source.target, source, self.data.selectedVersion, data)
}
if (self._view.compileIcon) {
self._view.compileIcon.style.color = styles.colors.black
self._view.compileIcon.classList.remove(`${css.spinningIcon}`)
self._view.compileIcon.classList.remove(`${css.bouncingIcon}`)
self._view.compileIcon.setAttribute('title', 'idle')
}
// reset the contractMetadata list (used by the publish action)
self.data.contractsDetails = {}
// refill the dropdown list
@ -132,8 +74,8 @@ module.exports = class CompileTab {
if (success) {
// TODO consider using compile tab as a proper module instead of just forwarding event
self._view.contractNames.removeAttribute('disabled')
self._components.compiler.visitContracts(contract => {
self.data.contractsDetails[contract.name] = parseContracts(contract.name, contract.object, self._components.compiler.getSource(contract.file))
self.compiler.visitContracts(contract => {
self.data.contractsDetails[contract.name] = parseContracts(contract.name, contract.object, self.compiler.getSource(contract.file))
var contractName = yo`<option>${contract.name}</option>`
self._view.contractNames.appendChild(contractName)
})
@ -154,7 +96,7 @@ module.exports = class CompileTab {
}
if (data.errors && data.errors.length) {
error = true
data.errors.forEach(function (err) {
data.errors.forEach((err) => {
if (self._deps.config.get('hideWarnings')) {
if (err.severity !== 'warning') {
self._deps.renderer.error(err.formattedMessage, self._view.errorContainer, {type: err.severity})
@ -165,24 +107,22 @@ module.exports = class CompileTab {
})
}
if (!error && data.contracts) {
self._components.compiler.visitContracts((contract) => {
self.compiler.visitContracts((contract) => {
self._deps.renderer.error(contract.name, self._view.errorContainer, {type: 'success'})
})
}
})
// Run the compiler instead of trying to save the website
$(window).keydown(function (e) {
$(window).keydown((e) => {
// ctrl+s or command+s
if ((e.metaKey || e.ctrlKey) && e.keyCode === 83) {
e.preventDefault()
self.runCompiler()
self.compileTabLogic.runCompiler()
}
})
}
getCompilationResult (cb) {
cb(null, this._components.compiler.lastCompilationResult)
}
profile () {
return {
name: 'solidity',
@ -192,75 +132,11 @@ module.exports = class CompileTab {
description: 'compile solidity contracts'
}
}
addWarning (msg, settings) {
const self = this
self._deps.renderer.error(msg, self._view.errorContainerHead, settings)
}
render () {
const self = this
if (self._view.el) return self._view.el
function onchangeLoadVersion (event) {
self.data.selectedVersion = self._view.versionSelector.value
self._updateVersionSelector()
}
function onchangeOptimize (event) {
self.data.optimize = !!self._view.optimize.checked
self._components.queryParams.update({ optimize: self.data.optimize })
self._components.compiler.setOptimize(self.data.optimize)
self.runCompiler()
}
self._components.compiler.event.register('compilerLoaded', (version) => self.setVersionText(version))
self.fetchAllVersion((allversions, selectedVersion) => {
self.data.allversions = allversions
self.data.selectedVersion = selectedVersion
if (self._view.versionSelector) self._updateVersionSelector()
})
self._view.optimize = yo`<input onchange=${onchangeOptimize} id="optimize" type="checkbox">`
if (self.data.optimize) self._view.optimize.setAttribute('checked', '')
self._view.versionSelector = yo`
<select onchange=${onchangeLoadVersion} class="${css.select}" id="versionSelector" disabled>
<option disabled selected>Select new compiler version</option>
</select>`
self._view.version = yo`<span id="version"></span>`
self._view.warnCompilationSlow = yo`<i title="Compilation Slow" style="visibility:hidden" class="${css.warnCompilationSlow} fa fa-exclamation-triangle" aria-hidden="true"></i>`
self._view.compileIcon = yo`<i class="fa fa-refresh ${css.icon}" aria-hidden="true"></i>`
self._view.compileButton = yo`<div class="${css.compileButton}" onclick=${compile} id="compile" title="Compile source code">${self._view.compileIcon} Start to compile (Ctrl-S)</div>`
self._view.autoCompile = yo`<input class="${css.autocompile}" onchange=${updateAutoCompile} id="autoCompile" type="checkbox" title="Auto compile">`
self._view.hideWarningsBox = yo`<input class="${css.autocompile}" onchange=${hideWarnings} id="hideWarningsBox" type="checkbox" title="Hide warnings">`
if (self.data.autoCompile) self._view.autoCompile.setAttribute('checked', '')
if (self.data.hideWarnings) self._view.hideWarningsBox.setAttribute('checked', '')
self._view.compileContainer = yo`
<div class="${css.compileContainer}">
<div class="${css.info}">
<span>Current version:</span> ${self._view.version}
<div class="${css.crow}">
${self._view.versionSelector}
</div>
<div class="${css.compileButtons}">
<div class=${css.checkboxes}>
<div class="${css.autocompileContainer}">
${self._view.autoCompile}
<label for="autoCompile" class="${css.autocompileText}">Auto compile</label>
</div>
<div class="${css.optimizeContainer}">
<div>${self._view.optimize}</div>
<label for="optimize" class="${css.checkboxText}">Enable Optimization</label>
</div>
<div class=${css.hideWarningsContainer}>
${self._view.hideWarningsBox}
<label for="hideWarningsBox" class="${css.autocompileText}">Hide warnings</label>
</div>
</div>
${self._view.compileButton}
</div>
</div>
</div>`
self._view.errorContainer = yo`<div class='error'></div>`
self._view.errorContainerHead = yo`<div class='error'></div>`
self._view.contractNames = yo`<select class="${css.contractNames}" disabled></select>`
@ -284,7 +160,7 @@ module.exports = class CompileTab {
</div>`
self._view.el = yo`
<div class="${css.compileTabView}" id="compileTabView">
${self._view.compileContainer}
${this.compilerContainer.render()}
${self._view.contractEl}
${self._view.errorContainerHead}
${self._view.errorContainer}
@ -303,12 +179,6 @@ module.exports = class CompileTab {
'swarmLocation': 'Swarm url where all metadata information can be found (contract needs to be published first)',
'web3Deploy': 'Copy/paste this code to any JavaScript/Web3 console to deploy this contract'
}
function updateAutoCompile (event) { self._deps.config.set('autoCompile', self._view.autoCompile.checked) }
function compile (event) { self.runCompiler() }
function hideWarnings (event) {
self._deps.config.set('hideWarnings', self._view.hideWarningsBox.checked)
compile()
}
function getContractProperty (property) {
const select = self._view.contractNames
if (select.children.length > 0 && select.selectedIndex >= 0) {
@ -419,345 +289,7 @@ module.exports = class CompileTab {
}
return self._view.el
}
setVersionText (text) {
const self = this
self.data.version = text
if (self._view.version) self._view.version.innerText = text
}
_updateVersionSelector () {
const self = this
self._view.versionSelector.innerHTML = ''
self._view.versionSelector.appendChild(yo`<option disabled selected>Select new compiler version</option>`)
self.data.allversions.forEach(build => self._view.versionSelector.appendChild(yo`<option value=${build.path}>${build.longVersion}</option>`))
self._view.versionSelector.removeAttribute('disabled')
self._components.queryParams.update({ version: self.data.selectedVersion })
var url
if (self.data.selectedVersion === 'builtin') {
var location = window.document.location
location = location.protocol + '//' + location.host + '/' + location.pathname
if (location.endsWith('index.html')) location = location.substring(0, location.length - 10)
if (!location.endsWith('/')) location += '/'
url = location + 'soljson.js'
} else {
if (self.data.selectedVersion.indexOf('soljson') !== 0 || helper.checkSpecialChars(self.data.selectedVersion)) {
return console.log('loading ' + self.data.selectedVersion + ' not allowed')
}
url = `${self.data.baseurl}/${self.data.selectedVersion}`
}
var isFirefox = typeof InstallTrigger !== 'undefined'
if (document.location.protocol !== 'file:' && Worker !== undefined && isFirefox) {
// Workers cannot load js on "file:"-URLs and we get a
// "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
// resort to non-worker version in that case.
self._components.compiler.loadVersion(true, url)
self.setVersionText('(loading using worker)')
} else {
self._components.compiler.loadVersion(false, url)
self.setVersionText('(loading)')
}
}
fetchAllVersion (callback) {
var self = this
minixhr(`${self.data.baseurl}/list.json`, function (json, event) {
// @TODO: optimise and cache results to improve app loading times
var allversions, selectedVersion
if (event.type !== 'error') {
try {
const data = JSON.parse(json)
allversions = data.builds.slice().reverse()
selectedVersion = self.data.defaultVersion
if (self._components.queryParams.get().version) selectedVersion = self._components.queryParams.get().version
} catch (e) {
addTooltip('Cannot load compiler version list. It might have been blocked by an advertisement blocker. Please try deactivating any of them from this page and reload.')
}
} else {
allversions = [{ path: 'builtin', longVersion: 'latest local version' }]
selectedVersion = 'builtin'
}
callback(allversions, selectedVersion)
})
}
runCompiler () {
const self = this
self._deps.fileManager.saveCurrentFile()
self._deps.editor.clearAnnotations()
var currentFile = self._deps.config.get('currentFile')
if (currentFile) {
if (/.(.sol)$/.exec(currentFile)) {
// only compile *.sol file.
var target = currentFile
var sources = {}
var provider = self._deps.fileManager.fileProviderOf(currentFile)
if (provider) {
provider.get(target, (error, content) => {
if (error) {
console.log(error)
} else {
sources[target] = { content }
self._components.compiler.compile(sources, target)
}
})
} else {
console.log('cannot compile ' + currentFile + '. Does not belong to any explorer')
}
}
}
}
importExternal (url, cb) {
const self = this
self._components.compilerImport.import(url,
(loadingMsg) => {
addTooltip(loadingMsg)
},
(error, content, cleanUrl, type, url) => {
if (!error) {
if (self._deps.fileProviders[type]) {
self._deps.fileProviders[type].addReadOnly(cleanUrl, content, url)
}
cb(null, content)
} else {
cb(error)
}
})
}
importFileCb (url, filecb) {
const self = this
if (url.indexOf('/remix_tests.sol') !== -1) {
return filecb(null, remixTests.assertLibCode)
}
var provider = self._deps.fileManager.fileProviderOf(url)
if (provider) {
if (provider.type === 'localhost' && !provider.isConnected()) {
return filecb(`file provider ${provider.type} not available while trying to resolve ${url}`)
}
provider.exists(url, (error, exist) => {
if (error) return filecb(error)
if (exist) {
return provider.get(url, filecb)
} else {
self.importExternal(url, filecb)
}
})
} else if (self._components.compilerImport.isRelativeImport(url)) {
// try to resolve localhost modules (aka truffle imports)
var splitted = /([^/]+)\/(.*)$/g.exec(url)
async.tryEach([
(cb) => { self.importFileCb('localhost/installed_contracts/' + url, cb) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { self.importFileCb('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2], cb) } },
(cb) => { self.importFileCb('localhost/node_modules/' + url, cb) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { self.importFileCb('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2], cb) } }],
(error, result) => { filecb(error, result) }
)
} else {
self.importExternal(url, filecb)
}
}
}
const css = csjs`
.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;
}
.select {
font-weight: bold;
margin: 10px 0px;
${styles.rightPanel.settingsTab.dropdown_SelectCompiler};
}
.info {
${styles.rightPanel.settingsTab.box_SolidityVersionInfo}
margin-bottom: 1em;
word-break: break-word;
}
.compileTabView {
padding: 2%;
}
.contract {
display: block;
margin: 3% 0;
}
.compileContainer {
${styles.rightPanel.compileTab.box_CompileContainer};
margin-bottom: 2%;
}
.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 {
color: ${styles.rightPanel.compileTab.icon_WarnCompilation_Color};
margin-left: 1%;
}
.compileButtons {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: flex-end;
}
.name {
display: flex;
}
.size {
display: flex;
}
.checkboxes {
display: flex;
width: 100%;
justify-content: space-between;
flex-wrap: wrap;
}
.compileButton {
${styles.rightPanel.compileTab.button_Compile};
width: 100%;
margin: 15px 0 10px 0;
font-size: 12px;
}
.container {
${styles.rightPanel.compileTab.box_CompileContainer};
margin: 0;
margin-bottom: 2%;
}
.contractContainer {
display: flex;
align-items: center;
margin-bottom: 2%;
}
.optimizeContainer {
display: flex;
}
.contractNames {
${styles.rightPanel.compileTab.dropdown_CompileContract};
width:78%;
}
.contractHelperButtons {
display: flex;
cursor: pointer;
text-align: center;
justify-content: flex-end;
margin: 15px 15px 10px 0;
}
.copyButton {
${styles.rightPanel.compileTab.button_Publish};
padding: 0 7px;
min-width: 50px;
width: auto;
margin-left: 5px;
background-color: inherit;
border: inherit;
}
.bytecodeButton {
min-width: 80px;
}
.copyIcon {
margin-right: 5px;
}
.details {
${styles.rightPanel.compileTab.button_Details};
min-width: 70px;
width: 80px;
}
.publish {
display: flex;
align-items: center;
margin-left: 10px;
cursor: pointer;
}
.log {
${styles.rightPanel.compileTab.box_CompileContainer};
display: flex;
flex-direction: column;
margin-bottom: 5%;
overflow: visible;
}
.key {
margin-right: 5px;
color: ${styles.rightPanel.text_Primary};
text-transform: uppercase;
width: 100%;
}
.value {
display: flex;
width: 100%;
margin-top: 1.5%;
}
.questionMark {
margin-left: 2%;
cursor: pointer;
color: ${styles.rightPanel.icon_Color_TogglePanel};
}
.questionMark:hover {
color: ${styles.rightPanel.icon_HoverColor_TogglePanel};
}
.detailsJSON {
padding: 8px 0;
background-color: ${styles.rightPanel.modalDialog_BackgroundColor_Primary};
border: none;
color: ${styles.rightPanel.modalDialog_text_Secondary};
}
.icon {
margin-right: 0.3em;
}
.spinningIcon {
margin-right: .3em;
animation: spin 2s linear infinite;
}
.bouncingIcon {
margin-right: .3em;
animation: bounce 2s infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-webkit-keyframes bounce {
0% {
margin-bottom: 0;
color: ${styles.colors.transparent};
}
70% {
margin-bottom: 0;
color: ${styles.rightPanel.text_Secondary};
}
100% {
margin-bottom: 0;
color: ${styles.colors.transparent};
}
}
`
module.exports = CompileTab

@ -0,0 +1,98 @@
const async = require('async')
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.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 && !/.(.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.compiler.compile(sources, target)
})
}
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._deps.fileProviders[type]) {
this._deps.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._deps.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

@ -0,0 +1,227 @@
/* global Worker */
const yo = require('yo-yo')
var minixhr = require('minixhr')
var helper = require('../../../lib/helper')
const addTooltip = require('../../ui/tooltip')
const styleGuide = require('../../ui/styles-guide/theme-chooser')
const styles = styleGuide.chooser()
var css = require('../styles/compile-tab-styles')
class CompilerContainer {
constructor (compileTabLogic, editor, config, queryParams) {
this._view = {}
this.compileTabLogic = compileTabLogic
this.editor = editor
this.config = config
this.queryParams = queryParams
this.data = {
hideWarnings: config.get('hideWarnings') || false,
autoCompile: config.get('autoCompile'),
compileTimeout: null,
timeout: 300,
allversions: null,
selectedVersion: null,
defaultVersion: 'soljson-v0.5.1+commit.c8a2cb62.js', // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler
baseurl: 'https://solc-bin.ethereum.org/bin'
}
this.listenToEvents()
}
listenToEvents () {
this.editor.event.register('contentChanged', this.scheduleCompilation.bind(this))
this.editor.event.register('sessionSwitched', this.scheduleCompilation.bind(this))
this.compileTabLogic.compiler.event.register('compilationDuration', (speed) => {
if (!this._view.warnCompilationSlow) return
if (speed > 1000) {
const msg = `Last compilation took ${speed}ms. We suggest to turn off autocompilation.`
this._view.warnCompilationSlow.setAttribute('title', msg)
this._view.warnCompilationSlow.style.visibility = 'visible'
} else {
this._view.warnCompilationSlow.style.visibility = 'hidden'
}
})
this.editor.event.register('contentChanged', () => {
if (!this._view.compileIcon) return
this._view.compileIcon.classList.add(`${css.bouncingIcon}`) // @TODO: compileView tab
})
this.compileTabLogic.compiler.event.register('loadingCompiler', () => {
if (!this._view.compileIcon) return
this._view.compileIcon.classList.add(`${css.spinningIcon}`)
this._view.warnCompilationSlow.style.visibility = 'hidden'
this._view.compileIcon.setAttribute('title', 'compiler is loading, please wait a few moments.')
})
this.compileTabLogic.compiler.event.register('compilationStarted', () => {
if (!this._view.compileIcon) return
this._view.compileIcon.classList.remove(`${css.bouncingIcon}`)
this._view.compileIcon.classList.add(`${css.spinningIcon}`)
this._view.compileIcon.setAttribute('title', 'compiling...')
})
this.compileTabLogic.compiler.event.register('compilerLoaded', () => {
if (!this._view.compileIcon) return
this._view.compileIcon.classList.remove(`${css.spinningIcon}`)
this._view.compileIcon.setAttribute('title', '')
})
this.compileTabLogic.compiler.event.register('compilationFinished', (success, data, source) => {
if (!this._view.compileIcon) return
this._view.compileIcon.style.color = styles.colors.black
this._view.compileIcon.classList.remove(`${css.spinningIcon}`)
this._view.compileIcon.classList.remove(`${css.bouncingIcon}`)
this._view.compileIcon.setAttribute('title', 'idle')
})
}
render () {
this.compileTabLogic.compiler.event.register('compilerLoaded', (version) => this.setVersionText(version))
this.fetchAllVersion((allversions, selectedVersion) => {
this.data.allversions = allversions
this.data.selectedVersion = selectedVersion
if (this._view.versionSelector) this._updateVersionSelector()
})
this._view.warnCompilationSlow = yo`<i title="Compilation Slow" style="visibility:hidden" class="${css.warnCompilationSlow} fa fa-exclamation-triangle" aria-hidden="true"></i>`
this._view.compileIcon = yo`<i class="fa fa-refresh ${css.icon}" aria-hidden="true"></i>`
this._view.compileButton = yo`<div class="${css.compileButton}" onclick=${this.compile.bind(this)} id="compile" title="Compile source code">${this._view.compileIcon} Start to compile (Ctrl-S)</div>`
this._view.autoCompile = yo`<input class="${css.autocompile}" onchange=${this.updateAutoCompile.bind(this)} id="autoCompile" type="checkbox" title="Auto compile">`
this._view.hideWarningsBox = yo`<input class="${css.autocompile}" onchange=${this.hideWarnings.bind(this)} id="hideWarningsBox" type="checkbox" title="Hide warnings">`
if (this.data.autoCompile) this._view.autoCompile.setAttribute('checked', '')
if (this.data.hideWarnings) this._view.hideWarningsBox.setAttribute('checked', '')
this._view.optimize = yo`<input onchange=${this.onchangeOptimize.bind(this)} id="optimize" type="checkbox">`
if (this.compileTabLogic.optimize) this._view.optimize.setAttribute('checked', '')
this._view.versionSelector = yo`
<select onchange=${this.onchangeLoadVersion.bind(this)} class="${css.select}" id="versionSelector" disabled>
<option disabled selected>Select new compiler version</option>
</select>`
this._view.version = yo`<span id="version"></span>`
this._view.compileContainer = yo`
<div class="${css.compileContainer}">
<div class="${css.info}">
<span>Current version:</span> ${this._view.version}
<div class="${css.crow}">
${this._view.versionSelector}
</div>
<div class="${css.compileButtons}">
<div class=${css.checkboxes}>
<div class="${css.autocompileContainer}">
${this._view.autoCompile}
<label for="autoCompile" class="${css.autocompileText}">Auto compile</label>
</div>
<div class="${css.optimizeContainer}">
<div>${this._view.optimize}</div>
<label for="optimize" class="${css.checkboxText}">Enable Optimization</label>
</div>
<div class=${css.hideWarningsContainer}>
${this._view.hideWarningsBox}
<label for="hideWarningsBox" class="${css.autocompileText}">Hide warnings</label>
</div>
</div>
${this._view.compileButton}
</div>
</div>
</div>`
return this._view.compileContainer
}
updateAutoCompile (event) {
this.config.set('autoCompile', this._view.autoCompile.checked)
}
compile (event) {
this.compileTabLogic.runCompiler()
}
hideWarnings (event) {
this.config.set('hideWarnings', this._view.hideWarningsBox.checked)
this.compile()
}
onchangeOptimize () {
this.compileTabLogic.setOptimize(!!this._view.optimize.checked)
this.compileTabLogic.runCompiler()
}
onchangeLoadVersion (event) {
this.data.selectedVersion = this._view.versionSelector.value
this._updateVersionSelector()
}
_updateVersionSelector () {
this._view.versionSelector.innerHTML = ''
this._view.versionSelector.appendChild(yo`<option disabled selected>Select new compiler version</option>`)
this.data.allversions.forEach(build => this._view.versionSelector.appendChild(yo`<option value=${build.path}>${build.longVersion}</option>`))
this._view.versionSelector.removeAttribute('disabled')
this.queryParams.update({ version: this.data.selectedVersion })
var url
if (this.data.selectedVersion === 'builtin') {
var location = window.document.location
location = location.protocol + '//' + location.host + '/' + location.pathname
if (location.endsWith('index.html')) location = location.substring(0, location.length - 10)
if (!location.endsWith('/')) location += '/'
url = location + 'soljson.js'
} else {
if (this.data.selectedVersion.indexOf('soljson') !== 0 || helper.checkSpecialChars(this.data.selectedVersion)) {
return console.log('loading ' + this.data.selectedVersion + ' not allowed')
}
url = `${this.data.baseurl}/${this.data.selectedVersion}`
}
var isFirefox = typeof InstallTrigger !== 'undefined'
if (document.location.protocol !== 'file:' && Worker !== undefined && isFirefox) {
// Workers cannot load js on "file:"-URLs and we get a
// "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
// resort to non-worker version in that case.
this.compileTabLogic.compiler.loadVersion(true, url)
this.setVersionText('(loading using worker)')
} else {
this.compileTabLogic.compiler.loadVersion(false, url)
this.setVersionText('(loading)')
}
}
setVersionText (text) {
this.data.version = text
if (this._view.version) this._view.version.innerText = text
}
fetchAllVersion (callback) {
minixhr(`${this.data.baseurl}/list.json`, (json, event) => {
// @TODO: optimise and cache results to improve app loading times
var allversions, selectedVersion
if (event.type !== 'error') {
try {
const data = JSON.parse(json)
allversions = data.builds.slice().reverse()
selectedVersion = this.data.defaultVersion
if (this.queryParams.get().version) selectedVersion = this.queryParams.get().version
} catch (e) {
addTooltip('Cannot load compiler version list. It might have been blocked by an advertisement blocker. Please try deactivating any of them from this page and reload.')
}
} else {
allversions = [{ path: 'builtin', longVersion: 'latest local version' }]
selectedVersion = 'builtin'
}
callback(allversions, selectedVersion)
})
}
scheduleCompilation () {
if (!this.config.get('autoCompile')) return
if (this.data.compileTimeout) window.clearTimeout(this.data.compileTimeout)
this.data.compileTimeout = window.setTimeout(() => this.compileTabLogic.runCompiler(), this.data.timeout)
}
}
module.exports = CompilerContainer

@ -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) {

@ -0,0 +1,213 @@
const csjs = require('csjs-inject')
const styleGuide = require('../../ui/styles-guide/theme-chooser')
const styles = styleGuide.chooser()
const css = csjs`
.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;
}
.select {
font-weight: bold;
margin: 10px 0px;
${styles.rightPanel.settingsTab.dropdown_SelectCompiler};
}
.info {
${styles.rightPanel.settingsTab.box_SolidityVersionInfo}
margin-bottom: 1em;
word-break: break-word;
}
.compileTabView {
padding: 2%;
}
.contract {
display: block;
margin: 3% 0;
}
.compileContainer {
${styles.rightPanel.compileTab.box_CompileContainer};
margin-bottom: 2%;
}
.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 {
color: ${styles.rightPanel.compileTab.icon_WarnCompilation_Color};
margin-left: 1%;
}
.compileButtons {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: flex-end;
}
.name {
display: flex;
}
.size {
display: flex;
}
.checkboxes {
display: flex;
width: 100%;
justify-content: space-between;
flex-wrap: wrap;
}
.compileButton {
${styles.rightPanel.compileTab.button_Compile};
width: 100%;
margin: 15px 0 10px 0;
font-size: 12px;
}
.container {
${styles.rightPanel.compileTab.box_CompileContainer};
margin: 0;
margin-bottom: 2%;
}
.contractContainer {
display: flex;
align-items: center;
margin-bottom: 2%;
}
.optimizeContainer {
display: flex;
}
.contractNames {
${styles.rightPanel.compileTab.dropdown_CompileContract};
width:78%;
}
.contractHelperButtons {
display: flex;
cursor: pointer;
text-align: center;
justify-content: flex-end;
margin: 15px 15px 10px 0;
}
.copyButton {
${styles.rightPanel.compileTab.button_Publish};
padding: 0 7px;
min-width: 50px;
width: auto;
margin-left: 5px;
background-color: inherit;
border: inherit;
}
.bytecodeButton {
min-width: 80px;
}
.copyIcon {
margin-right: 5px;
}
.details {
${styles.rightPanel.compileTab.button_Details};
min-width: 70px;
width: 80px;
}
.publish {
display: flex;
align-items: center;
margin-left: 10px;
cursor: pointer;
}
.log {
${styles.rightPanel.compileTab.box_CompileContainer};
display: flex;
flex-direction: column;
margin-bottom: 5%;
overflow: visible;
}
.key {
margin-right: 5px;
color: ${styles.rightPanel.text_Primary};
text-transform: uppercase;
width: 100%;
}
.value {
display: flex;
width: 100%;
margin-top: 1.5%;
}
.questionMark {
margin-left: 2%;
cursor: pointer;
color: ${styles.rightPanel.icon_Color_TogglePanel};
}
.questionMark:hover {
color: ${styles.rightPanel.icon_HoverColor_TogglePanel};
}
.detailsJSON {
padding: 8px 0;
background-color: ${styles.rightPanel.modalDialog_BackgroundColor_Primary};
border: none;
color: ${styles.rightPanel.modalDialog_text_Secondary};
}
.icon {
margin-right: 0.3em;
}
.spinningIcon {
margin-right: .3em;
animation: spin 2s linear infinite;
}
.bouncingIcon {
margin-right: .3em;
animation: bounce 2s infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-webkit-keyframes bounce {
0% {
margin-bottom: 0;
color: ${styles.colors.transparent};
}
70% {
margin-bottom: 0;
color: ${styles.rightPanel.text_Secondary};
}
100% {
margin-bottom: 0;
color: ${styles.colors.transparent};
}
}
`
module.exports = css

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

@ -0,0 +1,57 @@
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var css = csjs`
.container {
position : static;
box-sizing : border-box;
display : flex;
flex-direction : column;
flex-wrap : wrap;
justify-content : space-between;
align-items : center;
align-content : space-around;
border : 2px solid black;
width : 400px;
padding : 50px;
background-color: #bfbfbf;
font-family : "Lucida Console", Monaco, monospace
}
`
class LandingPage {
constructor (sections) {
this.sections = sections
}
render () {
var totalLook = yo`
<div class=${css.container}>
<h1> Remix </h1>
<br>
<br>
<br>
</div>
`
for (var i = 0; i < this.sections.length; i++) {
totalLook.appendChild(yo`
<div>
${this.sections[i].render()}
</div>
`)
}
if (!this._view) {
this._view = totalLook
}
return this._view
}
update () {
yo.update(this._view, this.render())
}
}
module.exports = LandingPage

@ -0,0 +1,61 @@
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var css = csjs`
.item {
display : flex;
flex-direction : column;
align-items : center;
width : 400px;
padding : 50px;
background-color: #bfbfbf;
font-family : "Lucida Console", Monaco, monospace
}
a:link {
color : black;
text-decoration : none;
}
`
class Section {
constructor (title, actions) {
this.title = title
this.actions = actions
}
render () {
var sectionLook = yo`
<div class=${css.item}>
<h2> ${this.title} </h2>
<br>
<br>
<br>
</div>
`
for (var i = 0; i < this.actions.length; i++) {
if (this.actions[i].type === `callback`) {
sectionLook.appendChild(yo`
<div>
<span onclick= ${this.actions[i].payload} > ${this.actions[i].label} </span>
</div>
`)
} else if (this.actions[i].type === `link`) {
sectionLook.appendChild(yo`
<div>
<a href= ${this.actions[i].payload} target="_blank" > ${this.actions[i].label} </a>
</div>
`)
}
}
if (!this._view) {
this._view = sectionLook
}
return this._view
}
}
module.exports = Section

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

@ -27,7 +27,7 @@ const css = csjs`
`
/*
* opt:
* opt:
* minWidth : minimn width for panels
* x : position of gutter at load
*
@ -93,15 +93,15 @@ export default class PanelsResize {
return { panel1Width, panel2left, panel2Width }
}
window.addEventListener('resize', function (event){
window.addEventListener('resize', function (event) {
setPosition({ x: panel1.offsetLeft + panel1.clientWidth })
})
var dragbar = yo`<div onmousedown=${mousedown} class=${css.dragbar}></div>`
panel1.appendChild(dragbar)
setPosition(opt)
}
}
minimize () {
let panel1Width = 0

@ -1,3 +1,4 @@
/* global localStorage */
import { EventEmitter } from 'events'
export class Store {
@ -8,7 +9,7 @@ export class Store {
* @param {string} name The name of the store
* @param {T} initialState The initial state used if state is not available in `localStorage`
*/
static fromLocal(name, initialState) {
static fromLocal (name, initialState) {
const fromLocal = localStorage.getItem(name)
const intial = fromLocal ? JSON.parse(fromLocal) : initialState
return new Store(name, intial)
@ -20,20 +21,20 @@ export class Store {
* @param {string} name The name of the store
* @param {T} initialState The initial state of the store
*/
constructor(name, initialState) {
constructor (name, initialState) {
this.event = new EventEmitter()
this.name = name
this.state = initialState
}
/** Listen on event from the store */
get on() {
return this.event.on;
get on () {
return this.event.on
}
/** Liste once on event from the store */
get once() {
return this.event.once;
get once () {
return this.event.once
}
/**
@ -51,17 +52,17 @@ export class Store {
* @template Key key of `this.state`
* @param {Key} key A key of the state
*/
get(key) {
get (key) {
return this.state.entities[key]
}
/** Reset the state its initial value */
reset() {
reset () {
this.state = this.initialState
}
/** Dispatch an event with the new state */
dispatch() {
dispatch () {
this.event.emit('newState', this.state)
}
}
@ -74,8 +75,6 @@ export class Store {
* @property {Object} entities A map of ids and entities
*/
export class EntityStore extends Store {
/**
@ -83,7 +82,7 @@ export class EntityStore extends Store {
* @param {string} name The name of the store
* @param {EntityState} initialState The initial state used if state is not available in `localStorage`
*/
static fromLocal(name, initialState) {
static fromLocal (name, initialState) {
const fromLocal = localStorage.getItem(name)
const intial = fromLocal ? JSON.parse(fromLocal) : initialState
return new EntityStore(name, intial)
@ -94,43 +93,37 @@ export class EntityStore extends Store {
* @param {string} name The name of the store
* @param {EntityState} initialState The initial state used if state is not available in `localStorage`
*/
constructor(name, initialState) {
/*
constructor (name, initialState) {
super(name, initialState)
}
////////////
// GETTER //
////////////
*/
/** Tne entities as a Map */
get entities() {
get entities () {
return this.state.entities
}
/** List of all the ids */
get ids() {
get ids () {
return this.state.ids
}
/** List of all active ID */
get actives() {
get actives () {
return this.state.actives
}
/** Return the length of the entity collection */
get length() {
get length () {
return this.state.ids.length
}
/////////////
// SETTERS //
/////////////
/**
* Add a new entity to the state
* @param {Object} entity
*/
add(id, entity) {
add (id, entity) {
this.state.entities[id] = entity
this.state.ids.push(id)
this.event.emit('add', id, entity)
@ -140,7 +133,7 @@ export class EntityStore extends Store {
* Add entities to the state
* @param {Array} entities
*/
addEntities(entities) {
addEntities (entities) {
entities.forEach((entity) => { this.add(entity.profile.name, entity) })
}
@ -148,7 +141,7 @@ export class EntityStore extends Store {
* Remove an entity from the state
* @param {(string|number)} id The id of the entity to remove
*/
remove(id) {
remove (id) {
delete this.state.entities[id]
this.state.ids.splice(this.state.ids.indexOf(id), 1)
this.state.actives.splice(this.state.ids.indexOf(id), 1)
@ -172,7 +165,7 @@ export class EntityStore extends Store {
* Activate one or several entity from the state
* @param {((string|number))} ids An id or a list of id to activate
*/
activate(id) {
activate (id) {
this.state.actives.push(id)
this.event.emit('activate', id)
}
@ -181,20 +174,20 @@ export class EntityStore extends Store {
* Deactivate one or several entity from the state
* @param {(string|number))} ids An id or a list of id to deactivate
*/
deactivate(id) {
deactivate (id) {
this.state.actives.splice(this.state.actives.indexOf(id), 1)
this.event.emit('deactivate', id)
}
///////////
// /////////
// QUERY //
///////////
// /////////
/**
* Get one entity
* @param {(string|number)} id The id of the entity to get
*/
getOne(id) {
getOne (id) {
return this.state.entities[id]
}
@ -202,29 +195,25 @@ export class EntityStore extends Store {
* Get many entities as an array
* @param {(string|number)[]} ids An array of id of entity to get
*/
getMany(ids) {
getMany (ids) {
return ids.map(id => this.state.entities[id])
}
/** Get all the entities as an array */
getAll() {
getAll () {
return this.state.ids.map(id => this.state.entities[id])
}
/** Get all active entities */
getActives() {
getActives () {
return this.state.actives.map(id => this.state.entities[id])
}
////////////////
// CONDITIONS //
////////////////
/**
* Is the entity active
* @param {(string|number)} id The id of the entity to check
*/
isActive(id) {
isActive (id) {
return this.state.actives.includes(id)
}
@ -232,7 +221,7 @@ export class EntityStore extends Store {
* Is this id inside the store
* @param {(string|number)} id The id of the entity to check
*/
hasEntity(id) {
hasEntity (id) {
return this.state.ids.includes(id)
}
@ -240,7 +229,7 @@ export class EntityStore extends Store {
* Is the state empty
* @param {(string|number)} id The id of the entity to check
*/
isEmpty() {
isEmpty () {
return this.state.ids.length === 0
}
}
@ -249,7 +238,8 @@ export class EntityStore extends Store {
* Store the state of the stores into LocalStorage
* @param {Store[]} stores The list of stores to store into `localStorage`
*/
function localState(stores) {
/*
function localState (stores) {
stores.forEach(store => {
const name = store.name
store.on('newState', (state) => {
@ -257,3 +247,4 @@ function localState(stores) {
})
})
}
*/

Loading…
Cancel
Save