diff --git a/src/app/tabs/compile-tab.js b/src/app/tabs/compile-tab.js index 09ada87dde..abb7ef9963 100644 --- a/src/app/tabs/compile-tab.js +++ b/src/app/tabs/compile-tab.js @@ -20,9 +20,8 @@ const CompilerContainer = require('./compileTab/compilerContainer.js') class CompileTab { constructor (registry) { - const self = this - self.event = new EventEmitter() - self._view = { + this.event = new EventEmitter() + this._view = { el: null, warnCompilationSlow: null, errorContainer: null, @@ -30,10 +29,10 @@ class CompileTab { contractNames: null, contractEl: null } - self.queryParams = new QueryParams() + this.queryParams = new QueryParams() // dependencies - self._deps = { + this._deps = { editor: registry.get('editor').api, config: registry.get('config').api, renderer: registry.get('renderer').api, @@ -42,52 +41,62 @@ class CompileTab { fileProviders: registry.get('fileproviders').api, pluginManager: registry.get('pluginmanager').api } - self.data = { + this.data = { contractsDetails: {} } - this.compileTabLogic = new CompileTabLogic(self.queryParams, self._deps.fileManager, self._deps.editor, self._deps.config, self._deps.fileProviders) + this.compileTabLogic = new CompileTabLogic(this.queryParams, this._deps.fileManager, this._deps.editor, this._deps.config, this._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.compilerContainer = new CompilerContainer( + this.compileTabLogic, + this._deps.editor, + this._deps.config, + this.queryParams + ) this.listenToEvents() } - listenToEvents () { - const self = this + /************ + * EVENTS + */ - self.compiler.event.register('compilationStarted', () => { - if (self._view.errorContainer) { - self._view.errorContainer.innerHTML = '' - self._view.errorContainerHead.innerHTML = '' + listenToEvents () { + this.compiler.event.register('compilationStarted', () => { + if (this._view.errorContainer) { + this._view.errorContainer.innerHTML = '' + this._view.errorContainerHead.innerHTML = '' } }) - self.compiler.event.register('compilationFinished', (success, data, source) => { + + this._deps.fileManager.event.register('currentFileChanged', (name) => { + this.compilerContainer.currentFile = name + }) + this.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) - } - // reset the contractMetadata list (used by the publish action) - self.data.contractsDetails = {} - // refill the dropdown list - self._view.contractNames.innerHTML = '' - if (success) { - // TODO consider using compile tab as a proper module instead of just forwarding event - self._view.contractNames.removeAttribute('disabled') - self.compiler.visitContracts(contract => { - self.data.contractsDetails[contract.name] = parseContracts(contract.name, contract.object, self.compiler.getSource(contract.file)) - var contractName = yo`` - self._view.contractNames.appendChild(contractName) + this.event.emit('compilationFinished', source.target, source, this.data.selectedVersion, data) + // Store the contracts + this.data.contractsDetails = {} + this.compiler.visitContracts((contract) => { + this.data.contractsDetails[contract.name] = parseContracts( + contract.name, + contract.object, + this.compiler.getSource(contract.file) + ) }) - } else { - self._view.contractNames.setAttribute('disabled', true) } - var error = false + // Update contract Selection + const contractMap = this.compiler.getContracts() + const contractSelection = this.contractSelection(Object.keys(contractMap) || []) + yo.update(this._view.contractSelection, contractSelection) + + let error = false if (data['error']) { error = true - self._deps.renderer.error(data['error'].formattedMessage, self._view.errorContainer, {type: data['error'].severity || 'error'}) + this._deps.renderer.error(data['error'].formattedMessage, this._view.errorContainer, {type: data['error'].severity || 'error'}) if (data['error'].mode === 'panic') { return modalDialogCustom.alert(yo`
The compiler returned with the following internal error:
${data['error'].formattedMessage}.
@@ -99,18 +108,18 @@ class CompileTab { if (data.errors && data.errors.length) { error = true data.errors.forEach((err) => { - if (self._deps.config.get('hideWarnings')) { + if (this._deps.config.get('hideWarnings')) { if (err.severity !== 'warning') { - self._deps.renderer.error(err.formattedMessage, self._view.errorContainer, {type: err.severity}) + this._deps.renderer.error(err.formattedMessage, this._view.errorContainer, {type: err.severity}) } } else { - self._deps.renderer.error(err.formattedMessage, self._view.errorContainer, {type: err.severity}) + this._deps.renderer.error(err.formattedMessage, this._view.errorContainer, {type: err.severity}) } }) } if (!error && data.contracts) { - self.compiler.visitContracts((contract) => { - self._deps.renderer.error(contract.name, self._view.errorContainer, {type: 'success'}) + this.compiler.visitContracts((contract) => { + this._deps.renderer.error(contract.name, this._view.errorContainer, {type: 'success'}) }) } }) @@ -120,7 +129,7 @@ class CompileTab { // ctrl+s or command+s if ((e.metaKey || e.ctrlKey) && e.keyCode === 83) { e.preventDefault() - self.compileTabLogic.runCompiler() + this.compileTabLogic.runCompiler() } }) } @@ -136,38 +145,96 @@ class CompileTab { } } - render () { - const self = this - if (self._view.el) return self._view.el + /********* + * SUB-COMPONENTS + */ - self._view.errorContainer = yo`
` - self._view.errorContainerHead = yo`
` - self._view.contractNames = yo`` - self._view.contractEl = yo` -
-
- ${self._view.contractNames} -
- Swarm -
+ /** + * Section to select the compiled contract + * @param {string[]} contractList Names of the compiled contracts + */ + contractSelection(contractList = []) { + return contractList.length !== 0 + ? yo`
+ + +
+ + +
-
Details
-
- ABI -
-
- Bytecode + Copy to Clipboard : +
+ +
-
` - self._view.el = yo` -
- ${this.compilerContainer.render()} - ${self._view.contractEl} - ${self._view.errorContainerHead} - ${self._view.errorContainer} -
` +
+
` + : yo`
+ No Contract Compiled Yet +
` + } + + // TODO : Add success alert when compilation succeed + contractCompiledSuccess() { + return yo`` + } + // TODO : Add error alert when compilation failed + contractCompiledError() { + return yo`` + } + + /************ + * METHODS + */ + + publish() { + const selectContractNames = this._view.contractNames + if (selectContractNames.children.length > 0 && selectContractNames.selectedIndex >= 0) { + var contract = this.data.contractsDetails[selectContractNames.children[selectContractNames.selectedIndex].innerHTML] + if (contract.metadata === undefined || contract.metadata.length === 0) { + modalDialogCustom.alert('This contract may be abstract, may not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') + } else { + publishOnSwarm(contract, this._deps.fileManager, function (err, uploaded) { + if (err) { + try { + err = JSON.stringify(err) + } catch (e) {} + modalDialogCustom.alert(yo`Failed to publish metadata file to swarm, please check the Swarm gateways is available ( swarm-gateways.net ).
+ ${err}
`) + } else { + var result = yo`
${uploaded.map((value) => { + return yo`
${value.filename} :
${value.output.url}
` + })}
` + modalDialogCustom.alert(yo`Metadata published successfully.
${result}
`) + } + }, (item) => { // triggered each time there's a new verified publish (means hash correspond) + this._deps.swarmfileProvider.addReadOnly(item.hash, item.content) + }) + } + } + } + + details() { const help = { 'Assembly': 'Assembly opcodes describing the contract including corresponding solidity source code', 'Opcodes': 'Assembly opcodes describing the contract', @@ -182,115 +249,104 @@ 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 getContractProperty (property) { - const select = self._view.contractNames - if (select.children.length > 0 && select.selectedIndex >= 0) { - const contractName = select.children[select.selectedIndex].innerHTML - const contractProperties = self.data.contractsDetails[contractName] - return contractProperties[property] || null - } - } - function copyContractProperty (property) { - let content = getContractProperty(property) - if (!content) { - addTooltip('No content available for ' + property) - return - } - - try { - if (typeof content !== 'string') { - content = JSON.stringify(content, null, '\t') - } - } catch (e) {} + if (!this.selectedContract) throw new Error('No contract compiled yet') + const contractProperties = this.data.contractsDetails[this.selectedContract] + const log = yo`
` + Object.keys(contractProperties).map(propertyName => { + const copyDetails = yo`${copyToClipboard(() => contractProperties[propertyName])}` + const questionMark = yo`` + log.appendChild(yo`
+
${propertyName} ${copyDetails} ${questionMark}
+ ${this.insertValue(contractProperties, propertyName)} +
`) + }) + modalDialog(this.selectedContract, log, { label: '' }, { label: 'Close' }) + } - copy(content) - addTooltip('Copied value to clipboard') - } - function copyABI () { - copyContractProperty('abi') - } - function copyBytecode () { - copyContractProperty('bytecode') - } - function details () { - const select = self._view.contractNames - if (select.children.length > 0 && select.selectedIndex >= 0) { - const contractName = select.children[select.selectedIndex].innerHTML - const contractProperties = self.data.contractsDetails[contractName] - const log = yo`
` - Object.keys(contractProperties).map(propertyName => { - const copyDetails = yo`${copyToClipboard(() => contractProperties[propertyName])}` - const questionMark = yo`` - log.appendChild(yo`
-
${propertyName} ${copyDetails} ${questionMark}
- ${insertValue(contractProperties, propertyName)} -
`) - }) - modalDialog(contractName, log, { label: '' }, { label: 'Close' }) - } - } - function insertValue (details, propertyName) { - var node - if (propertyName === 'web3Deploy' || propertyName === 'name' || propertyName === 'Assembly') { - node = yo`
${details[propertyName]}
` - } else if (propertyName === 'abi' || propertyName === 'metadata') { - const treeView = new TreeView({ - extractData: function (item, parent, key) { - var ret = {} - if (item instanceof Array) { - ret.children = item.map((item, index) => ({ key: index, value: item })) - ret.self = '' - } else if (item instanceof Object) { - ret.children = Object.keys(item).map((key) => ({key: key, value: item[key]})) - ret.self = '' - } else { - ret.self = item - ret.children = [] - } - return ret - } - }) - if (details[propertyName] !== '') { - try { - node = yo`
${treeView.render(typeof details[propertyName] === 'object' ? details[propertyName] : JSON.parse(details[propertyName]))}
` // catch in case the parsing fails. - } catch (e) { - node = yo`
Unable to display "${propertyName}": ${e.message}
` + insertValue (details, propertyName) { + var node + if (propertyName === 'web3Deploy' || propertyName === 'name' || propertyName === 'Assembly') { + node = yo`
${details[propertyName]}
` + } else if (propertyName === 'abi' || propertyName === 'metadata') { + const treeView = new TreeView({ + extractData: function (item, parent, key) { + var ret = {} + if (item instanceof Array) { + ret.children = item.map((item, index) => ({ key: index, value: item })) + ret.self = '' + } else if (item instanceof Object) { + ret.children = Object.keys(item).map((key) => ({key: key, value: item[key]})) + ret.self = '' + } else { + ret.self = item + ret.children = [] } - } else { - node = yo`
-
` + return ret + } + }) + if (details[propertyName] !== '') { + try { + node = yo`
${treeView.render(typeof details[propertyName] === 'object' ? details[propertyName] : JSON.parse(details[propertyName]))}
` // catch in case the parsing fails. + } catch (e) { + node = yo`
Unable to display "${propertyName}": ${e.message}
` } } else { - node = yo`
${JSON.stringify(details[propertyName], null, 4)}
` + node = yo`
-
` } - return yo`
${node || ''}
` + } else { + node = yo`
${JSON.stringify(details[propertyName], null, 4)}
` } - function publish () { - const selectContractNames = self._view.contractNames - if (selectContractNames.children.length > 0 && selectContractNames.selectedIndex >= 0) { - var contract = self.data.contractsDetails[selectContractNames.children[selectContractNames.selectedIndex].innerHTML] - if (contract.metadata === undefined || contract.metadata.length === 0) { - modalDialogCustom.alert('This contract may be abstract, may not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') - } else { - publishOnSwarm(contract, self._deps.fileManager, function (err, uploaded) { - if (err) { - try { - err = JSON.stringify(err) - } catch (e) {} - modalDialogCustom.alert(yo`Failed to publish metadata file to swarm, please check the Swarm gateways is available ( swarm-gateways.net ).
- ${err}
`) - } else { - var result = yo`
${uploaded.map((value) => { - return yo`
${value.filename} :
${value.output.url}
` - })}
` - modalDialogCustom.alert(yo`Metadata published successfully.
${result}
`) - } - }, function (item) { // triggered each time there's a new verified publish (means hash correspond) - self._deps.swarmfileProvider.addReadOnly(item.hash, item.content) - }) - } - } + return yo`
${node || ''}
` + } + + getContractProperty (property) { + if (!this.selectedContract) throw new Error('No contract compiled yet') + const contractProperties = this.data.contractsDetails[this.selectedContract] + return contractProperties[property] || null + } + + copyContractProperty (property) { + let content = getContractProperty(property) + if (!content) { + addTooltip('No content available for ' + property) + return } - return self._view.el + + try { + if (typeof content !== 'string') { + content = JSON.stringify(content, null, '\t') + } + } catch (e) {} + + copy(content) + addTooltip('Copied value to clipboard') + } + + copyABI () { + this.copyContractProperty('abi') + } + + copyBytecode () { + this.copyContractProperty('bytecode') + } + + render () { + if (this._view.el) return this._view.el + + this._view.errorContainer = yo`
` + this._view.errorContainerHead = yo`
` + this._view.contractSelection = this.contractSelection() + this._view.compilerContainer = this.compilerContainer.render() + this.compilerContainer.currentFile = this._deps.fileManager.currentFile() + + this._view.el = yo` +
+ ${this._view.compilerContainer} + ${this._view.contractSelection} + ${this._view.errorContainerHead} + ${this._view.errorContainer} +
` + return this._view.el } }