/* global alert, confirm, prompt, Option, Worker, chrome */ 'use strict' var async = require('async') var $ = require('jquery') var base64 = require('js-base64').Base64 var swarmgw = require('swarmgw') var csjs = require('csjs-inject') var QueryParams = require('./app/query-params') var queryParams = new QueryParams() var GistHandler = require('./app/gist-handler') var gistHandler = new GistHandler() var Storage = require('./app/storage') var Files = require('./app/files') var Config = require('./app/config') var Editor = require('./app/editor') var Renderer = require('./app/renderer') var Compiler = require('./app/compiler') var ExecutionContext = require('./app/execution-context') var UniversalDApp = require('./universal-dapp.js') var Debugger = require('./app/debugger') var FormalVerification = require('./app/formalVerification') var EventManager = require('./lib/eventManager') var StaticAnalysis = require('./app/staticanalysis/staticAnalysisView') var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter') var FilePanel = require('./app/file-panel') var examples = require('./app/example-contracts') // 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 var loadFilesCallback = function (files) { filesToLoad = files } // will be replaced later window.addEventListener('message', function (ev) { if (typeof ev.data === typeof [] && ev.data[0] === 'loadFiles') { loadFilesCallback(ev.data[1]) } }, false) /* trigger tabChanged */ var run = function () { var self = this this.event = new EventManager() var storage = new Storage() var files = new Files(storage) var config = new Config(storage) var currentFile // return all the files, except the temporary/readonly ones function packageFiles () { var ret = {} Object.keys(files.list()) .filter(function (path) { if (!files.isReadOnly(path)) { return path } }) .map(function (path) { ret[path] = { content: files.get(path) } }) return ret } function createNonClashingName (path) { var counter = '' while (files.exists(path + counter)) { counter = (counter | 0) + 1 } return path + counter } // Add files received from remote instance (i.e. another browser-solidity) function loadFiles (filesSet) { for (var f in filesSet) { files.set(createNonClashingName(f), filesSet[f].content) } switchToNextFile() } // Replace early callback with instant response loadFilesCallback = function (files) { loadFiles(files) } // Run if we did receive an event from remote instance while starting up if (filesToLoad !== null) { loadFiles(filesToLoad) } // ------------------ gist load ---------------- var loadingFromGist = gistHandler.handleLoad(queryParams.get(), function (gistId) { $.ajax({ url: 'https://api.github.com/gists/' + gistId, jsonp: 'callback', dataType: 'jsonp', success: function (response) { if (response.data) { if (!response.data.files) { alert('Gist load error: ' + response.data.message) return } loadFiles(response.data.files) } } }) }) // insert ballot contract if there are no files available if (!loadingFromGist && Object.keys(files.list()).length === 0) { if (!files.set(examples.ballot.name, examples.ballot.content)) { alert('Failed to store example contract in browser. Remix will not work properly. Please ensure Remix has access to LocalStorage. Safari in Private mode is known not to work.') } } // ----------------- Chrome cloud storage sync -------------------- function chromeCloudSync () { if (typeof chrome === 'undefined' || !chrome || !chrome.storage || !chrome.storage.sync) { return } var obj = {} var done = false var count = 0 function check (key) { chrome.storage.sync.get(key, function (resp) { console.log('comparing to cloud', key, resp) if (typeof resp[key] !== 'undefined' && obj[key] !== resp[key] && confirm('Overwrite "' + key + '"? Click Ok to overwrite local file with file from cloud. Cancel will push your local file to the cloud.')) { console.log('Overwriting', key) files.set(key, resp[key]) refreshTabs() } else { console.log('add to obj', obj, key) obj[key] = files.get(key) } done++ if (done >= count) { chrome.storage.sync.set(obj, function () { console.log('updated cloud files with: ', obj, this, arguments) }) } }) } for (var y in files.list()) { console.log('checking', y) obj[y] = files.get(y) count++ check(y) } } window.syncStorage = chromeCloudSync chromeCloudSync() // ----------------- editor ---------------------- var editor = new Editor(document.getElementById('input')) // ---------------- FilePanel -------------------- /**************************************************************************** @TODO's 1. I would put a virtual file called Summary as the root entry of the treeview, which displays the list of the files with the size in bytes of each 2. drag'n'drop to enable to rename files&folders in the file explorer into different sub folders 3. I would put a virtual file called `Summary` as the root entry of the treeview, which displays the list of the files with the size in bytes of each. 4. add maybe more tape tests 5. gist imports + copy to the browser => phase of writing 6. add filemanagement from righthand panel to filepanel compoennt (editing/imports/exports, public gist, load from github, create new project, ... setup load and modify files) */ // var sources = { // 'test/client/credit.sol': '', // 'src/voting.sol': '', // 'src/leasing.sol': '', // 'src/gmbh/contract.sol': false, // 'src/gmbh/test.sol': false, // 'src/gmbh/company.sol': false, // 'src/gmbh/node_modules/ballot.sol': false, // 'src/ug/finance.sol': false, // 'app/solidity/mode.sol': true, // 'app/ethereum/constitution.sol': true // } // Object.keys(sources).forEach(function (key) { files.set(key, sources[key]) }) /****************************************************************************/ var css = csjs` .filepanel { display : flex; width : 200px; } ` var filepanel = document.querySelector('#filepanel') filepanel.className = css.filepanel var FilePanelAPI = { createName: createNonClashingName, switchToFile: switchToFile } var el = new FilePanel(FilePanelAPI, files) filepanel.appendChild(el) var api = el.api api.register('ui', function changeLayout (data) { var value if (data.type === 'minimize') { value = -parseInt(window['filepanel'].style.width) value = (isNaN(value) ? -window['filepanel'].getBoundingClientRect().width : value) window['filepanel'].style.position = 'absolute' window['filepanel'].style.left = (value - 5) + 'px' window['filepanel'].style.width = -value + 'px' window['tabs-bar'].style.left = '45px' } else if (data.type === 'maximize') { value = -parseInt(window['filepanel'].style.left) + 'px' window['filepanel'].style.position = 'static' window['filepanel'].style.width = value window['filepanel'].style.left = '' window['tabs-bar'].style.left = value } else { window['filepanel'].style.width = data.width + 'px' window['tabs-bar'].style.left = data.width + 'px' } }) api.register('focus', function (path) { [...window.files.querySelectorAll('.file .name')].forEach(function (span) { if (span.innerText === path) switchToFile(path) // @TODO: scroll into view }) }) files.event.register('fileRenamed', function (oldName, newName) { [...window.files.querySelectorAll('.file .name')].forEach(function (span) { if (span.innerText === oldName) span.innerText = newName }) }) // ------------------ gist publish -------------- $('#gist').click(function () { if (confirm('Are you sure you want to publish all your files anonymously as a public gist on github.com?')) { var files = packageFiles() var description = 'Created using browser-solidity: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://ethereum.github.io/browser-solidity/#version=' + queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&gist=' $.ajax({ url: 'https://api.github.com/gists', type: 'POST', data: JSON.stringify({ description: description, public: true, files: files }) }).done(function (response) { if (response.html_url && confirm('Created a gist at ' + response.html_url + ' Would you like to open it in a new window?')) { window.open(response.html_url, '_blank') } }).fail(function (xhr, text, err) { alert('Failed to create gist: ' + (err || 'Unknown transport error')) }) } }) $('#copyOver').click(function () { var target = prompt( 'To which other browser-solidity instance do you want to copy over all files?', 'https://ethereum.github.io/browser-solidity/' ) if (target === null) { return } var files = packageFiles() $('