From 871f31c6f92c719942b19bc41aab3e250cc14d3d Mon Sep 17 00:00:00 2001 From: Dave Hoover Date: Fri, 27 May 2016 13:09:16 -0500 Subject: [PATCH 01/14] Initial refactoring effort: extract methods into modules --- src/app.js | 137 +++++++------------------------------ src/app/gist-handler.js | 30 ++++++++ src/app/query-params.js | 36 ++++++++++ src/app/storage-handler.js | 40 +++++++++++ 4 files changed, 131 insertions(+), 112 deletions(-) create mode 100644 src/app/gist-handler.js create mode 100644 src/app/query-params.js create mode 100644 src/app/storage-handler.js diff --git a/src/app.js b/src/app.js index f4d11ba38f..0cbcc2673a 100644 --- a/src/app.js +++ b/src/app.js @@ -4,6 +4,10 @@ var web3 = require('./web3-adapter.js'); var ace = require('brace'); require('./mode-solidity.js'); +var queryParams = require('./app/query-params'); +var gistHandler = require('./app/gist-handler'); +var StorageHandler = require('./app/storage-handler'); + // 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; @@ -20,55 +24,15 @@ var run = function() { // ------------------ query params (hash) ---------------- - function getQueryParams() { - - var qs = window.location.hash.substr(1); - - if (window.location.search.length > 0) { - // use legacy query params instead of hash - window.location.hash = window.location.search.substr(1); - window.location.search = ""; - } - - var params = {}; - var parts = qs.split("&"); - for (var x in parts) { - var keyValue = parts[x].split("="); - if (keyValue[0] !== "") params[keyValue[0]] = keyValue[1]; - } - return params; - } - - function updateQueryParams(params) { - var currentParams = getQueryParams(); - var keys = Object.keys(params); - for (var x in keys) { - currentParams[keys[x]] = params[keys[x]]; - } - var queryString = "#"; - var updatedKeys = Object.keys(currentParams); - for( var y in updatedKeys) { - queryString += updatedKeys[y] + "=" + currentParams[updatedKeys[y]] + "&"; - } - window.location.hash = queryString.slice(0, -1); - } - - function syncQueryParams() { - $('#optimize').attr( 'checked', (getQueryParams().optimize == "true") ); + $('#optimize').attr( 'checked', (queryParams.get().optimize == "true") ); } - window.onhashchange = syncQueryParams; syncQueryParams(); // ------------------ gist load ---------------- - function getGistId(str) { - var idr = /[0-9A-Fa-f]{8,}/; - var match = idr.exec(str); - return match ? match[0] : null; - } function loadFiles(files) { for (var f in files) { var key = fileKey(f); @@ -85,25 +49,12 @@ var run = function() { updateFiles(); } - var queryParams = getQueryParams(); - var loadingFromGist = false; - if (typeof queryParams['gist'] != undefined) { - var gistId; - if (queryParams['gist'] === '') { - var str = prompt("Enter the URL or ID of the Gist you would like to load."); - if (str !== '') { - gistId = getGistId( str ); - loadingFromGist = !!gistId; - } - } else { - gistId = queryParams['gist']; - loadingFromGist = !!gistId; - } - if (loadingFromGist) $.ajax({ - url: 'https://api.github.com/gists/'+gistId, - jsonp: 'callback', - dataType: 'jsonp', - success: function(response){ + gistHandler.handleLoad(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 ); @@ -111,9 +62,9 @@ var run = function() { } loadFiles(response.data.files); } - } - }); - } + } + }); + }); loadFilesCallback = function(files) { loadFiles(files); @@ -123,52 +74,15 @@ var run = function() { // ----------------- storage -------------------- - function syncStorage() { - - if (typeof chrome === 'undefined' || !chrome || !chrome.storage || !chrome.storage.sync) return; - - var obj = {}; - var done = false; - var count = 0; - var dont = 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 '" + fileNameFromKey(key) + "'? Click Ok to overwrite local file with file from cloud. Cancel will push your local file to the cloud.")) { - console.log("Overwriting", key ); - localStorage.setItem( key, resp[key] ); - updateFiles(); - } else { - console.log( "add to obj", obj, key); - obj[key] = localStorage[key]; - } - done++; - if (done >= count) chrome.storage.sync.set( obj, function(){ - console.log( "updated cloud files with: ", obj, this, arguments); - }); - }); - } - - for (var y in window.localStorage) { - console.log("checking", y); - obj[y] = window.localStorage.getItem(y); - if (y.indexOf(SOL_CACHE_FILE_PREFIX) !== 0) continue; - count++; - check(y); - } - - - } - - window.syncStorage = syncStorage; - syncStorage(); + var SOL_CACHE_FILE_PREFIX = 'sol-cache-file-'; + var storageHandler = new StorageHandler(SOL_CACHE_FILE_PREFIX); + window.syncStorage = storageHandler.sync; + storageHandler.sync(); // ----------------- editor ---------------------- - var SOL_CACHE_FILE_PREFIX = 'sol-cache-file-'; var SOL_CACHE_UNTITLED = SOL_CACHE_FILE_PREFIX + 'Untitled'; var SOL_CACHE_FILE = null; @@ -285,7 +199,7 @@ var run = 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=" + getQueryParams().version + "&optimize="+ getQueryParams().optimize +"&gist="; + 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', @@ -516,7 +430,7 @@ var run = function() { } }); $('#versionSelector').change(function() { - updateQueryParams({version: $('#versionSelector').val() }); + queryParams.update({version: $('#versionSelector').val() }); loadVersion($('#versionSelector').val()); }); @@ -626,7 +540,7 @@ var run = function() { if (input === null) { renderError(error); } else { - var optimize = getQueryParams().optimize; + var optimize = queryParams.get().optimize; compileJSON(input, optimize ? 1 : 0); } }); @@ -788,7 +702,7 @@ var run = function() { var worker = null; var loadVersion = function(version) { $('#version').text("(loading)"); - updateQueryParams({version: version}); + queryParams.update({version: version}); var isFirefox = typeof InstallTrigger !== 'undefined'; if (document.location.protocol != 'file:' && Worker !== undefined && isFirefox) { // Workers cannot load js on "file:"-URLs and we get a @@ -811,7 +725,7 @@ var run = function() { } }; - loadVersion( getQueryParams().version || 'soljson-latest.js'); + loadVersion( queryParams.get().version || 'soljson-latest.js'); editor.getSession().on('change', onChange); editor.on('changeSession', function(){ @@ -820,7 +734,7 @@ var run = function() { }); document.querySelector('#optimize').addEventListener('change', function(){ - updateQueryParams({optimize: document.querySelector('#optimize').checked }); + queryParams.update({optimize: document.querySelector('#optimize').checked }); compile(); }); @@ -1041,8 +955,7 @@ var run = function() { return funABI; }; - syncStorage(); - + storageHandler.sync(); }; module.exports = { diff --git a/src/app/gist-handler.js b/src/app/gist-handler.js new file mode 100644 index 0000000000..2cfb99bc71 --- /dev/null +++ b/src/app/gist-handler.js @@ -0,0 +1,30 @@ +var queryParams = require('./query-params'); + +function handleLoad(cb) { + var params = queryParams.get(); + var loadingFromGist = false; + if (typeof params['gist'] != undefined) { + var gistId; + if (params['gist'] === '') { + var str = prompt("Enter the URL or ID of the Gist you would like to load."); + if (str !== '') { + gistId = getGistId( str ); + loadingFromGist = !!gistId; + } + } else { + gistId = params['gist']; + loadingFromGist = !!gistId; + } + if (loadingFromGist) cb(gistId); + } +} + +function getGistId(str) { + var idr = /[0-9A-Fa-f]{8,}/; + var match = idr.exec(str); + return match ? match[0] : null; +} + +module.exports = { + handleLoad: handleLoad +}; diff --git a/src/app/query-params.js b/src/app/query-params.js new file mode 100644 index 0000000000..4789b965db --- /dev/null +++ b/src/app/query-params.js @@ -0,0 +1,36 @@ +function getQueryParams() { + var qs = window.location.hash.substr(1); + + if (window.location.search.length > 0) { + // use legacy query params instead of hash + window.location.hash = window.location.search.substr(1); + window.location.search = ""; + } + + var params = {}; + var parts = qs.split("&"); + for (var x in parts) { + var keyValue = parts[x].split("="); + if (keyValue[0] !== "") params[keyValue[0]] = keyValue[1]; + } + return params; +} + +function updateQueryParams(params) { + var currentParams = getQueryParams(); + var keys = Object.keys(params); + for (var x in keys) { + currentParams[keys[x]] = params[keys[x]]; + } + var queryString = "#"; + var updatedKeys = Object.keys(currentParams); + for( var y in updatedKeys) { + queryString += updatedKeys[y] + "=" + currentParams[updatedKeys[y]] + "&"; + } + window.location.hash = queryString.slice(0, -1); +} + +module.exports = { + get: getQueryParams, + update: updateQueryParams, +}; \ No newline at end of file diff --git a/src/app/storage-handler.js b/src/app/storage-handler.js new file mode 100644 index 0000000000..a4ae12d67e --- /dev/null +++ b/src/app/storage-handler.js @@ -0,0 +1,40 @@ +function StorageHandler(SOL_CACHE_FILE_PREFIX) { + + this.sync = function() { + + if (typeof chrome === 'undefined' || !chrome || !chrome.storage || !chrome.storage.sync) return; + + var obj = {}; + var done = false; + var count = 0 + var dont = 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 '" + fileNameFromKey(key) + "'? Click Ok to overwrite local file with file from cloud. Cancel will push your local file to the cloud.")) { + console.log("Overwriting", key ); + localStorage.setItem( key, resp[key] ); + updateFiles(); + } else { + console.log( "add to obj", obj, key); + obj[key] = localStorage[key]; + } + done++; + if (done >= count) chrome.storage.sync.set( obj, function(){ + console.log( "updated cloud files with: ", obj, this, arguments); + }) + }) + } + + for (var y in window.localStorage) { + console.log("checking", y); + obj[y] = window.localStorage.getItem(y); + if (y.indexOf(SOL_CACHE_FILE_PREFIX) !== 0) continue; + count++; + check(y); + } + }; +} + +module.exports = StorageHandler; From d49162d7af8a245baef4cede761ae07d6873670d Mon Sep 17 00:00:00 2001 From: Dave Hoover Date: Fri, 27 May 2016 19:11:45 -0500 Subject: [PATCH 02/14] Editor extraction --- src/app.js | 131 +++++++++------------------------------- src/app/editor.js | 128 +++++++++++++++++++++++++++++++++++++++ src/app/gist-handler.js | 3 +- 3 files changed, 158 insertions(+), 104 deletions(-) create mode 100644 src/app/editor.js diff --git a/src/app.js b/src/app.js index 0cbcc2673a..d9f759cddb 100644 --- a/src/app.js +++ b/src/app.js @@ -1,12 +1,11 @@ var $ = require('jquery'); var UniversalDApp = require('./universal-dapp.js'); var web3 = require('./web3-adapter.js'); -var ace = require('brace'); -require('./mode-solidity.js'); var queryParams = require('./app/query-params'); var gistHandler = require('./app/gist-handler'); var StorageHandler = require('./app/storage-handler'); +var Editor = require('./app/editor'); // The event listener needs to be registered as early as possible, because the // parent will send the message upon the "load" event. @@ -45,11 +44,11 @@ var run = function() { } window.localStorage[key] = content; } - SOL_CACHE_FILE = fileKey(Object.keys(files)[0]); + editor.setCacheFile(fileKey(Object.keys(files)[0])); updateFiles(); } - gistHandler.handleLoad(function(gistId) { + var loadingFromGist = gistHandler.handleLoad(function(gistId) { $.ajax({ url: 'https://api.github.com/gists/'+gistId, jsonp: 'callback', @@ -83,46 +82,7 @@ var run = function() { // ----------------- editor ---------------------- - var SOL_CACHE_UNTITLED = SOL_CACHE_FILE_PREFIX + 'Untitled'; - var SOL_CACHE_FILE = null; - - var editor = ace.edit("input"); - var sessions = {}; - - var Range = ace.acequire('ace/range').Range; - var errMarkerId = null; - - var untitledCount = ''; - if (!getFiles().length || window.localStorage['sol-cache']) { - if(loadingFromGist) return; - // Backwards-compatibility - while (window.localStorage[SOL_CACHE_UNTITLED + untitledCount]) - untitledCount = (untitledCount - 0) + 1; - SOL_CACHE_FILE = SOL_CACHE_UNTITLED + untitledCount; - window.localStorage[SOL_CACHE_FILE] = window.localStorage['sol-cache'] || BALLOT_EXAMPLE; - window.localStorage.removeItem('sol-cache'); - } - - SOL_CACHE_FILE = getFiles()[0]; - - - var files = getFiles(); - for (var x in files) { - sessions[files[x]] = newEditorSession(files[x]); - } - - editor.setSession( sessions[SOL_CACHE_FILE] ); - editor.resize(true); - - function newEditorSession(filekey) { - var s = new ace.EditSession(window.localStorage[filekey], "ace/mode/javascript"); - s.setUndoManager(new ace.UndoManager()); - s.setTabSize(4); - s.setUseSoftTabs(true); - sessions[filekey] = s; - return s; - } - + var editor = new Editor(ace, loadingFromGist, SOL_CACHE_FILE_PREFIX); // ----------------- tabbed menu ------------------- @@ -185,7 +145,7 @@ var run = function() { var packageFiles = function() { var files = {}; - var filesArr = getFiles(); + var filesArr = editor.getFiles(); for (var f in filesArr) { files[fileNameFromKey(filesArr[f])] = { @@ -236,12 +196,7 @@ var run = function() { var FILE_SCROLL_DELTA = 300; $('.newFile').on('click', function() { - untitledCount = ''; - while (window.localStorage[SOL_CACHE_UNTITLED + untitledCount]) - untitledCount = (untitledCount - 0) + 1; - SOL_CACHE_FILE = SOL_CACHE_UNTITLED + untitledCount; - sessions[SOL_CACHE_FILE] = null; - window.localStorage[SOL_CACHE_FILE] = ''; + editor.newFile(); updateFiles(); $filesEl.animate({left: Math.max( (0 - activeFilePos() + (FILE_SCROLL_DELTA/2)), 0)+ "px"}, "slow", function(){ @@ -274,7 +229,7 @@ var run = function() { var content = window.localStorage.getItem( fileKey(originalName) ); window.localStorage[fileKey( newName )] = content; window.localStorage.removeItem( fileKey( originalName) ); - SOL_CACHE_FILE = fileKey( newName ); + editor.setCacheFile(fileKey( newName )); } updateFiles(); @@ -287,11 +242,10 @@ var run = function() { $filesEl.on('click', '.file .remove', function(ev) { ev.preventDefault(); var name = $(this).parent().find('.name').text(); - var index = getFiles().indexOf( fileKey(name) ); if (confirm("Are you sure you want to remove: " + name + " from local storage?")) { window.localStorage.removeItem( fileKey( name ) ); - SOL_CACHE_FILE = getFiles()[ Math.max(0, index - 1)]; + editor.setNextFile(fileKey(name)); updateFiles(); } return false; @@ -299,7 +253,7 @@ var run = function() { function showFileHandler(ev) { ev.preventDefault(); - SOL_CACHE_FILE = fileKey( $(this).find('.name').text() ); + editor.setCacheFile(fileKey( $(this).find('.name').text() )); updateFiles(); return false; } @@ -312,7 +266,7 @@ var run = function() { function updateFiles() { var $filesEl = $('#files'); - var files = getFiles(); + var files = editor.getFiles(); $filesEl.find('.file').remove(); @@ -320,14 +274,13 @@ var run = function() { $filesEl.append(fileTabTemplate(files[f])); } - if (SOL_CACHE_FILE) { - var active = fileTabFromKey(SOL_CACHE_FILE); + if (editor.cacheFileIsPresent()) { + var active = fileTabFromKey(editor.getCacheFile()); active.addClass('active'); - editor.setSession( sessions[SOL_CACHE_FILE] ); - editor.focus(); + editor.resetSession(); } - $('#input').toggle( !!SOL_CACHE_FILE ); - $('#output').toggle( !!SOL_CACHE_FILE ); + $('#input').toggle( editor.cacheFileIsPresent() ); + $('#output').toggle( editor.cacheFileIsPresent() ); reAdjust(); } @@ -344,18 +297,6 @@ var run = function() { return key.replace( SOL_CACHE_FILE_PREFIX, '' ); } - function getFiles() { - var files = []; - for (var f in localStorage ) { - if (f.indexOf( SOL_CACHE_FILE_PREFIX, 0 ) === 0) { - files.push(f); - if (!sessions[f]) sessions[f] = newEditorSession(f); - } - } - return files; - } - - $filesWrapper = $('.files-wrapper'); $scrollerRight = $('.scroller-right'); $scrollerLeft = $('.scroller-left'); @@ -500,16 +441,6 @@ var run = function() { function onResize() { editor.resize(); - var session = editor.getSession(); - session.setUseWrapMode(document.querySelector('#editorWrap').checked); - if(session.getUseWrapMode()) { - var characterWidth = editor.renderer.characterWidth; - var contentWidth = editor.container.ownerDocument.getElementsByClassName("ace_scroller")[0].clientWidth; - - if(contentWidth > 0) { - session.setWrapLimit(parseInt(contentWidth / characterWidth, 10)); - } - } reAdjust(); } window.onresize = onResize; @@ -526,15 +457,14 @@ var run = function() { var previousInput = ''; var sourceAnnotations = []; var compile = function(missingInputs) { - editor.getSession().clearAnnotations(); + editor.clearAnnotations(); sourceAnnotations = []; - editor.getSession().removeMarker(errMarkerId); $('#output').empty(); var input = editor.getValue(); - window.localStorage.setItem(SOL_CACHE_FILE, input); + window.localStorage.setItem(editor.getCacheFile(), input); var files = {}; - files[fileNameFromKey(SOL_CACHE_FILE)] = input; + files[fileNameFromKey(editor.getCacheFile())] = input; gatherImports(files, missingInputs, function(input, error) { $('#output').empty(); if (input === null) { @@ -577,7 +507,7 @@ var run = function() { var onChange = function() { var input = editor.getValue(); if (input === "") { - window.localStorage.setItem(SOL_CACHE_FILE, ''); + window.localStorage.setItem(editor.getCacheFile(), ''); return; } if (input === previousInput) @@ -627,7 +557,7 @@ var run = function() { importHints = importHints || []; if (!compilerAcceptsMultipleFiles) { - cb(files[fileNameFromKey(SOL_CACHE_FILE)]); + cb(files[fileNameFromKey(editor.getCacheFile())]); return; } var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g; @@ -642,10 +572,10 @@ var run = function() { while (importHints.length > 0) { var m = importHints.pop(); if (m in files) continue; - if (getFiles().indexOf(fileKey(m)) !== -1) { + if (editor.getFiles().indexOf(fileKey(m)) !== -1) { files[m] = window.localStorage[fileKey(m)]; reloop = true; - } else if (m.startsWith('./') && getFiles().indexOf(fileKey(m.slice(2))) !== -1) { + } else if (m.startsWith('./') && editor.getFiles().indexOf(fileKey(m.slice(2))) !== -1) { files[m] = window.localStorage[fileKey(m.slice(2))]; reloop = true; } else if (m in cachedRemoteFiles) { @@ -727,11 +657,7 @@ var run = function() { loadVersion( queryParams.get().version || 'soljson-latest.js'); - editor.getSession().on('change', onChange); - editor.on('changeSession', function(){ - editor.getSession().on('change', onChange); - onChange(); - }); + editor.onChangeSetup(onChange); document.querySelector('#optimize').addEventListener('change', function(){ queryParams.update({optimize: document.querySelector('#optimize').checked }); @@ -755,24 +681,23 @@ var run = function() { var errFile = err[1]; var errLine = parseInt(err[2], 10) - 1; var errCol = err[4] ? parseInt(err[4], 10) : 0; - if (errFile == '' || errFile == fileNameFromKey(SOL_CACHE_FILE)) { + if (errFile == '' || errFile == fileNameFromKey(editor.getCacheFile())) { sourceAnnotations[sourceAnnotations.length] = { row: errLine, column: errCol, text: message, type: type }; - editor.getSession().setAnnotations(sourceAnnotations); + editor.setAnnotations(sourceAnnotations); } $error.click(function(ev){ - if (errFile != '' && errFile != fileNameFromKey(SOL_CACHE_FILE) && getFiles().indexOf(fileKey(errFile)) !== -1) { + if (errFile != '' && errFile != fileNameFromKey(editor.getCacheFile()) && editor.getFiles().indexOf(fileKey(errFile)) !== -1) { // Switch to file - SOL_CACHE_FILE = fileKey(errFile); + editor.setCacheFile(fileKey(errFile)); updateFiles(); //@TODO could show some error icon in files with errors } - editor.focus(); - editor.gotoLine(errLine + 1, errCol - 1, true); + editor.handleErrorClick(errLine, errCol); }); $error.find('.close').click(function(ev){ ev.preventDefault(); diff --git a/src/app/editor.js b/src/app/editor.js new file mode 100644 index 0000000000..ae16a23da2 --- /dev/null +++ b/src/app/editor.js @@ -0,0 +1,128 @@ +var ace = require('brace'); +require('../mode-solidity.js'); + +function Editor(ace, loadingFromGist, SOL_CACHE_FILE_PREFIX) { + + this.newFile = function() { + untitledCount = ''; + while (window.localStorage[SOL_CACHE_UNTITLED + untitledCount]) + untitledCount = (untitledCount - 0) + 1; + SOL_CACHE_FILE = SOL_CACHE_UNTITLED + untitledCount; + sessions[SOL_CACHE_FILE] = null; + window.localStorage[SOL_CACHE_FILE] = ''; + }; + + this.setCacheFile = function(cacheFile) { + SOL_CACHE_FILE = cacheFile; + }; + + this.getCacheFile = function() { + return SOL_CACHE_FILE; + }; + + this.cacheFileIsPresent = function() { + return !!SOL_CACHE_FILE; + }; + + this.setNextFile = function(fileKey) { + var index = this.getFiles().indexOf( fileKey ); + this.setCacheFile(this.getFiles()[ Math.max(0, index - 1)]); + }; + + this.resetSession = function() { + editor.setSession( sessions[SOL_CACHE_FILE] ); + editor.focus(); + + }; + + this.getFiles = function() { + var files = []; + for (var f in localStorage ) { + if (f.indexOf( SOL_CACHE_FILE_PREFIX, 0 ) === 0) { + files.push(f); + if (!sessions[f]) sessions[f] = newEditorSession(f); + } + } + return files; + } + + this.resize = function() { + editor.resize(); + var session = editor.getSession(); + session.setUseWrapMode(document.querySelector('#editorWrap').checked); + if(session.getUseWrapMode()) { + var characterWidth = editor.renderer.characterWidth; + var contentWidth = editor.container.ownerDocument.getElementsByClassName("ace_scroller")[0].clientWidth; + + if(contentWidth > 0) { + session.setWrapLimit(parseInt(contentWidth / characterWidth, 10)); + } + } + }; + + this.getValue = function() { + return editor.getValue(); + }; + + this.clearAnnotations = function() { + editor.getSession().clearAnnotations(); + }; + + this.setAnnotations = function(sourceAnnotations) { + editor.getSession().setAnnotations(sourceAnnotations); + }; + + this.onChangeSetup = function(onChange) { + editor.getSession().on('change', onChange); + editor.on('changeSession', function(){ + editor.getSession().on('change', onChange); + onChange(); + }) + }; + + this.handleErrorClick = function(errLine, errCol) { + editor.focus(); + editor.gotoLine(errLine + 1, errCol - 1, true); + }; + + function newEditorSession(filekey) { + var s = new ace.EditSession(window.localStorage[filekey], "ace/mode/javascript") + s.setUndoManager(new ace.UndoManager()); + s.setTabSize(4); + s.setUseSoftTabs(true); + sessions[filekey] = s; + return s; + } + + function setupStuff(files) { + var untitledCount = ''; + if (!files.length || window.localStorage['sol-cache']) { + if(loadingFromGist) return; + // Backwards-compatibility + while (window.localStorage[SOL_CACHE_UNTITLED + untitledCount]) + untitledCount = (untitledCount - 0) + 1; + SOL_CACHE_FILE = SOL_CACHE_UNTITLED + untitledCount; + window.localStorage[SOL_CACHE_FILE] = window.localStorage['sol-cache'] || BALLOT_EXAMPLE; + window.localStorage.removeItem('sol-cache'); + } + + SOL_CACHE_FILE = files[0]; + + for (var x in files) { + sessions[files[x]] = newEditorSession(files[x]) + } + + editor.setSession( sessions[SOL_CACHE_FILE] ); + editor.resize(true); + } + + var SOL_CACHE_UNTITLED = SOL_CACHE_FILE_PREFIX + 'Untitled'; + var SOL_CACHE_FILE = null; + + var editor = ace.edit("input"); + var sessions = {}; + + setupStuff(this.getFiles()); +} + +module.exports = Editor; diff --git a/src/app/gist-handler.js b/src/app/gist-handler.js index 2cfb99bc71..486b2dce6e 100644 --- a/src/app/gist-handler.js +++ b/src/app/gist-handler.js @@ -16,7 +16,8 @@ function handleLoad(cb) { loadingFromGist = !!gistId; } if (loadingFromGist) cb(gistId); - } + } + return loadingFromGist; } function getGistId(str) { From 289cec440f84674e262fe64e4cd3d45fa38afa34 Mon Sep 17 00:00:00 2001 From: Dave Hoover Date: Fri, 27 May 2016 19:18:35 -0500 Subject: [PATCH 03/14] Removing unnecessary Editor constructor argument --- src/app.js | 2 +- src/app/editor.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app.js b/src/app.js index d9f759cddb..b93df63207 100644 --- a/src/app.js +++ b/src/app.js @@ -82,7 +82,7 @@ var run = function() { // ----------------- editor ---------------------- - var editor = new Editor(ace, loadingFromGist, SOL_CACHE_FILE_PREFIX); + var editor = new Editor(loadingFromGist, SOL_CACHE_FILE_PREFIX); // ----------------- tabbed menu ------------------- diff --git a/src/app/editor.js b/src/app/editor.js index ae16a23da2..f71416920a 100644 --- a/src/app/editor.js +++ b/src/app/editor.js @@ -1,7 +1,7 @@ var ace = require('brace'); require('../mode-solidity.js'); -function Editor(ace, loadingFromGist, SOL_CACHE_FILE_PREFIX) { +function Editor(loadingFromGist, SOL_CACHE_FILE_PREFIX) { this.newFile = function() { untitledCount = ''; From fbe522ebc0c63b9fad1d56e88fc65a39dea60175 Mon Sep 17 00:00:00 2001 From: Dave Hoover Date: Sat, 28 May 2016 14:16:28 -0500 Subject: [PATCH 04/14] Need to provide updateFiles to StorageHandler --- src/app.js | 2 +- src/app/storage-handler.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app.js b/src/app.js index b93df63207..9921c362eb 100644 --- a/src/app.js +++ b/src/app.js @@ -75,7 +75,7 @@ var run = function() { var SOL_CACHE_FILE_PREFIX = 'sol-cache-file-'; - var storageHandler = new StorageHandler(SOL_CACHE_FILE_PREFIX); + var storageHandler = new StorageHandler(updateFiles, SOL_CACHE_FILE_PREFIX); window.syncStorage = storageHandler.sync; storageHandler.sync(); diff --git a/src/app/storage-handler.js b/src/app/storage-handler.js index a4ae12d67e..c68c340716 100644 --- a/src/app/storage-handler.js +++ b/src/app/storage-handler.js @@ -1,4 +1,4 @@ -function StorageHandler(SOL_CACHE_FILE_PREFIX) { +function StorageHandler(updateFiles, SOL_CACHE_FILE_PREFIX) { this.sync = function() { From b57d751da25161fdc5403bf6ef7e29b08a9ebd67 Mon Sep 17 00:00:00 2001 From: Dave Hoover Date: Mon, 30 May 2016 08:01:34 -0500 Subject: [PATCH 05/14] Extracted compiler --- src/app.js | 271 +++++++++----------------------------------- src/app/compiler.js | 203 +++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+), 220 deletions(-) create mode 100644 src/app/compiler.js diff --git a/src/app.js b/src/app.js index 9921c362eb..6c01020d7b 100644 --- a/src/app.js +++ b/src/app.js @@ -4,8 +4,10 @@ var web3 = require('./web3-adapter.js'); var queryParams = require('./app/query-params'); var gistHandler = require('./app/gist-handler'); + var StorageHandler = require('./app/storage-handler'); var Editor = require('./app/editor'); +var Compiler = require('./app/compiler'); // The event listener needs to be registered as early as possible, because the // parent will send the message upon the "load" event. @@ -118,7 +120,7 @@ var run = function() { $web3Toggle.on('change', executionContextChange ); $web3endpoint.on('change', function() { setProviderFromEndpoint(); - if (executionContext == 'web3') compile(); + if (executionContext == 'web3') compiler.compile(); }); function executionContextChange (ev) { @@ -129,7 +131,7 @@ var run = function() { executionContext = ev.target.value; setProviderFromEndpoint(); } - compile(); + compiler.compile(); } function setProviderFromEndpoint() { @@ -433,7 +435,7 @@ var run = function() { setEditorSize( hidingRHP ? 0 : window.localStorage[EDITOR_SIZE_CACHE_KEY] ); $('.toggleRHP i').toggleClass('fa-angle-double-right', !hidingRHP); $('.toggleRHP i').toggleClass('fa-angle-double-left', hidingRHP); - if (!hidingRHP) compile(); + if (!hidingRHP) compiler.compile(); }); @@ -450,220 +452,6 @@ var run = function() { document.querySelector('#editorWrap').addEventListener('change', onResize); - // ----------------- compiler ---------------------- - var compileJSON; - var compilerAcceptsMultipleFiles; - - var previousInput = ''; - var sourceAnnotations = []; - var compile = function(missingInputs) { - editor.clearAnnotations(); - sourceAnnotations = []; - $('#output').empty(); - var input = editor.getValue(); - window.localStorage.setItem(editor.getCacheFile(), input); - - var files = {}; - files[fileNameFromKey(editor.getCacheFile())] = input; - gatherImports(files, missingInputs, function(input, error) { - $('#output').empty(); - if (input === null) { - renderError(error); - } else { - var optimize = queryParams.get().optimize; - compileJSON(input, optimize ? 1 : 0); - } - }); - }; - var compilationFinished = function(result, missingInputs) { - var data; - var noFatalErrors = true; // ie warnings are ok - - try { - data = $.parseJSON(result); - } catch (exception) { - renderError('Invalid JSON output from the compiler: ' + exception); - return; - } - - if (data['error'] !== undefined) { - renderError(data['error']); - if (errortype(data['error']) !== 'warning') noFatalErrors = false; - } - if (data['errors'] != undefined) { - $.each(data['errors'], function(i, err) { - renderError(err); - if (errortype(err) !== 'warning') noFatalErrors = false; - }); - } - - if (missingInputs !== undefined && missingInputs.length > 0) - compile(missingInputs); - else if (noFatalErrors && !hidingRHP) - renderContracts(data, editor.getValue()); - }; - - var compileTimeout = null; - var onChange = function() { - var input = editor.getValue(); - if (input === "") { - window.localStorage.setItem(editor.getCacheFile(), ''); - return; - } - if (input === previousInput) - return; - previousInput = input; - if (compileTimeout) window.clearTimeout(compileTimeout); - compileTimeout = window.setTimeout(compile, 300); - }; - - var onCompilerLoaded = function() { - if (worker === null) { - var compile; - var missingInputs = []; - if ('_compileJSONCallback' in Module) { - compilerAcceptsMultipleFiles = true; - var missingInputsCallback = Module.Runtime.addFunction(function(path, contents, error) { - missingInputs.push(Module.Pointer_stringify(path)); - }); - var compileInternal = Module.cwrap("compileJSONCallback", "string", ["string", "number", "number"]); - compile = function(input, optimize) { - missingInputs.length = 0; - return compileInternal(input, optimize, missingInputsCallback); - }; - } else if ('_compileJSONMulti' in Module) { - compilerAcceptsMultipleFiles = true; - compile = Module.cwrap("compileJSONMulti", "string", ["string", "number"]); - } else { - compilerAcceptsMultipleFiles = false; - compile = Module.cwrap("compileJSON", "string", ["string", "number"]); - } - compileJSON = function(source, optimize, cb) { - try { - var result = compile(source, optimize); - } catch (exception) { - result = JSON.stringify({error: 'Uncaught JavaScript exception:\n' + exception}); - } - compilationFinished(result, missingInputs); - }; - $('#version').text(Module.cwrap("version", "string", [])()); - } - previousInput = ''; - onChange(); - }; - - var cachedRemoteFiles = {}; - function gatherImports(files, importHints, cb) { - importHints = importHints || []; - if (!compilerAcceptsMultipleFiles) - { - cb(files[fileNameFromKey(editor.getCacheFile())]); - return; - } - var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g; - var reloop = false; - do { - reloop = false; - for (var fileName in files) { - var match; - while (match = importRegex.exec(files[fileName])) - importHints.push(match[1]); - } - while (importHints.length > 0) { - var m = importHints.pop(); - if (m in files) continue; - if (editor.getFiles().indexOf(fileKey(m)) !== -1) { - files[m] = window.localStorage[fileKey(m)]; - reloop = true; - } else if (m.startsWith('./') && editor.getFiles().indexOf(fileKey(m.slice(2))) !== -1) { - files[m] = window.localStorage[fileKey(m.slice(2))]; - reloop = true; - } else if (m in cachedRemoteFiles) { - files[m] = cachedRemoteFiles[m]; - reloop = true; - } else if (githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m)) { - $('#output').append($('
').append($('
').text("Loading github.com/" + githubMatch[3] + " ...")));
-					$.getJSON('https://api.github.com/repos/' + githubMatch[3] + '/contents/' + githubMatch[4], function(result) {
-						if ('content' in result)
-						{
-							var content = Base64.decode(result.content);
-							cachedRemoteFiles[m] = content;
-							files[m] = content;
-							gatherImports(files, importHints, cb);
-						}
-						else
-							cb(null, "Unable to import \"" + m + "\"");
-					}).fail(function(){
-						cb(null, "Unable to import \"" + m + "\"");
-					});
-					return;
-				} else {
-					cb(null, "Unable to import \"" + m + "\"");
-					return;
-				}
-			}
-		} while (reloop);
-		cb(JSON.stringify({'sources':files}));
-	}
-
-	var initializeWorker = function() {
-		if (worker !== null)
-			worker.terminate();
-		worker = new Worker('worker.js');
-		worker.addEventListener('message', function(msg) {
-			var data = msg.data;
-			switch (data.cmd) {
-			case 'versionLoaded':
-				$('#version').text(data.data);
-				compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles;
-				onCompilerLoaded();
-				break;
-			case 'compiled':
-				compilationFinished(data.data, data.missingInputs);
-				break;
-			}
-		});
-		worker.onerror = function(msg) { console.log(msg.data); };
-		worker.addEventListener('error', function(msg) { console.log(msg.data); });
-		compileJSON = function(source, optimize) {
-			worker.postMessage({cmd: 'compile', source: source, optimize: optimize});
-		};
-	};
-	var worker = null;
-	var loadVersion = function(version) {
-		$('#version').text("(loading)");
-		queryParams.update({version: version});
-		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.
-			initializeWorker();
-			worker.postMessage({cmd: 'loadVersion', data: 'https://ethereum.github.io/solc-bin/bin/' + version});
-		} else {
-			Module = null;
-			compileJSON = function(source, optimize) { compilationFinished('{}'); };
-			var newScript = document.createElement('script');
-			newScript.type = 'text/javascript';
-			newScript.src = 'https://ethereum.github.io/solc-bin/bin/' + version;
-			document.getElementsByTagName("head")[0].appendChild(newScript);
-			var check = window.setInterval(function() {
-				if (!Module) return;
-				window.clearInterval(check);
-				onCompilerLoaded();
-			}, 200);
-		}
-	};
-
-	loadVersion( queryParams.get().version || 'soljson-latest.js');
-
-	editor.onChangeSetup(onChange);
-
-	document.querySelector('#optimize').addEventListener('change', function(){
-		queryParams.update({optimize: document.querySelector('#optimize').checked });
-		compile();
-	});
-
 	// ----------------- compiler output renderer ----------------------
 	var detailsOpen = {};
 
@@ -682,13 +470,12 @@ var run = function() {
 			var errLine = parseInt(err[2], 10) - 1;
 			var errCol = err[4] ? parseInt(err[4], 10) : 0;
 			if (errFile == '' || errFile == fileNameFromKey(editor.getCacheFile())) {
-				sourceAnnotations[sourceAnnotations.length] = {
+				compiler.addAnnotation({
 					row: errLine,
 					column: errCol,
 					text: message,
 					type: type
-				};
-				editor.setAnnotations(sourceAnnotations);
+				});
 			}
 			$error.click(function(ev){
 				if (errFile != '' && errFile != fileNameFromKey(editor.getCacheFile()) && editor.getFiles().indexOf(fileKey(errFile)) !== -1) {
@@ -880,6 +667,50 @@ var run = function() {
 		return funABI;
 	};
 
+	// ----------------- compiler ----------------------
+
+	function handleGithubCall(root, path, cb) {
+		$('#output').append($('
').append($('
').text("Loading github.com/" + root + " ...")));
+    return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path, cb);
+	}
+
+	var compiler = new Compiler(editor, renderContracts, renderError, errortype, fileNameFromKey, fileKey, handleGithubCall, $('#output'), function() { return hidingRHP; });
+
+	function setVersionText(text) {
+		$('#version').text(text);
+	}
+
+	var loadVersion = function(version) {
+		setVersionText("(loading)");
+		queryParams.update({version: version});
+		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.
+			compiler.initializeWorker(version, setVersionText);
+		} else {
+			Module = null;
+			compiler.setCompileJSON()
+			var newScript = document.createElement('script');
+			newScript.type = 'text/javascript';
+			newScript.src = 'https://ethereum.github.io/solc-bin/bin/' + version;
+			document.getElementsByTagName("head")[0].appendChild(newScript);
+			var check = window.setInterval(function() {
+				if (!Module) return;
+				window.clearInterval(check);
+				compiler.onCompilerLoaded(setVersionText);
+			}, 200);
+		}
+	};
+
+	loadVersion( queryParams.get().version || 'soljson-latest.js');
+
+	document.querySelector('#optimize').addEventListener('change', function(){
+		queryParams.update({optimize: document.querySelector('#optimize').checked });
+		compiler.compile();
+	});
+
 	storageHandler.sync();
 };
 
diff --git a/src/app/compiler.js b/src/app/compiler.js
new file mode 100644
index 0000000000..fd9ce09e96
--- /dev/null
+++ b/src/app/compiler.js
@@ -0,0 +1,203 @@
+var queryParams = require('./query-params');
+
+function Compiler(editor, renderContracts, renderError, errortype, fileNameFromKey, fileKey, handleGithubCall, outputField, hidingRHP) {
+
+  var compileJSON;
+  var compilerAcceptsMultipleFiles;
+
+  var previousInput = '';
+  var sourceAnnotations = [];
+
+  var cachedRemoteFiles = {};
+  var worker = null;
+
+  var compileTimeout = null;
+
+  function onChange() {
+    var input = editor.getValue();
+    if (input === "") {
+      window.localStorage.setItem(editor.getCacheFile(), '');
+      return;
+    }
+    if (input === previousInput)
+      return;
+    previousInput = input;
+    if (compileTimeout) window.clearTimeout(compileTimeout);
+    compileTimeout = window.setTimeout(compile, 300);
+  }
+
+  editor.onChangeSetup(onChange);
+
+  var compile = function(missingInputs) {
+    editor.clearAnnotations();
+    sourceAnnotations = [];
+    outputField.empty();
+    var input = editor.getValue();
+    window.localStorage.setItem(editor.getCacheFile(), input);
+
+    var files = {};
+    files[fileNameFromKey(editor.getCacheFile())] = input;
+    gatherImports(files, missingInputs, function(input, error) {
+      outputField.empty();
+      if (input === null) {
+        renderError(error);
+      } else {
+        var optimize = queryParams.get().optimize;
+        compileJSON(input, optimize ? 1 : 0);
+      }
+    });
+  };
+  this.compile = compile;
+
+  this.addAnnotation = function(annotation) {
+    sourceAnnotations[sourceAnnotations.length] = annotation;
+    editor.setAnnotations(sourceAnnotations);
+  };
+
+  this.setCompileJSON = function() {
+    compileJSON = function(source, optimize) { compilationFinished('{}'); };  
+  };
+
+  function onCompilerLoaded(setVersionText) {
+    if (worker === null) {
+      var compile;
+      var missingInputs = [];
+      if ('_compileJSONCallback' in Module) {
+        compilerAcceptsMultipleFiles = true;
+        var missingInputsCallback = Module.Runtime.addFunction(function(path, contents, error) {
+          missingInputs.push(Module.Pointer_stringify(path));
+        });
+        var compileInternal = Module.cwrap("compileJSONCallback", "string", ["string", "number", "number"]);
+        compile = function(input, optimize) {
+          missingInputs.length = 0;
+          return compileInternal(input, optimize, missingInputsCallback);
+        };
+      } else if ('_compileJSONMulti' in Module) {
+        compilerAcceptsMultipleFiles = true;
+        compile = Module.cwrap("compileJSONMulti", "string", ["string", "number"]);
+      } else {
+        compilerAcceptsMultipleFiles = false;
+        compile = Module.cwrap("compileJSON", "string", ["string", "number"]);
+      }
+      compileJSON = function(source, optimize, cb) {
+        try {
+          var result = compile(source, optimize);
+        } catch (exception) {
+          result = JSON.stringify({error: 'Uncaught JavaScript exception:\n' + exception});
+        }
+        compilationFinished(result, missingInputs);
+      };
+      setVersionText(Module.cwrap("version", "string", [])());
+    }
+    previousInput = '';
+    onChange();
+  };
+  this.onCompilerLoaded = onCompilerLoaded;
+
+  function compilationFinished(result, missingInputs) {
+    var data;
+    var noFatalErrors = true; // ie warnings are ok
+
+    try {
+      data = JSON.parse(result);
+    } catch (exception) {
+      renderError('Invalid JSON output from the compiler: ' + exception);
+      return;
+    }
+
+    if (data['error'] !== undefined) {
+      renderError(data['error']);
+      if (errortype(data['error']) !== 'warning') noFatalErrors = false;
+    }
+    if (data['errors'] != undefined) {
+      data['errors'].forEach(function(err) {
+        renderError(err);
+        if (errortype(err) !== 'warning') noFatalErrors = false;
+      });
+    }
+
+    if (missingInputs !== undefined && missingInputs.length > 0)
+      this.compile(missingInputs);
+    else if (noFatalErrors && !hidingRHP())
+      renderContracts(data, editor.getValue());
+  }
+
+  this.initializeWorker = function(version, setVersionText) {
+    if (worker !== null)
+      worker.terminate();
+    worker = new Worker('worker.js');
+    worker.addEventListener('message', function(msg) {
+      var data = msg.data;
+      switch (data.cmd) {
+      case 'versionLoaded':
+        setVersionText(data.data);
+        compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles;
+        onCompilerLoaded(setVersionText);
+        break;
+      case 'compiled':
+        compilationFinished(data.data, data.missingInputs);
+        break;
+      };
+    });
+    worker.onerror = function(msg) { console.log(msg.data); };
+    worker.addEventListener('error', function(msg) { console.log(msg.data); });
+    compileJSON = function(source, optimize) {
+      worker.postMessage({cmd: 'compile', source: source, optimize: optimize});
+    };
+    worker.postMessage({cmd: 'loadVersion', data: 'https://ethereum.github.io/solc-bin/bin/' + version});
+  };
+
+  function gatherImports(files, importHints, cb) {
+    importHints = importHints || [];
+    if (!compilerAcceptsMultipleFiles)
+    {
+      cb(files[fileNameFromKey(editor.getCacheFile())]);
+      return;
+    }
+    var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g;
+    var reloop = false;
+    do {
+      reloop = false;
+      for (var fileName in files) {
+        var match;
+        while (match = importRegex.exec(files[fileName]))
+          importHints.push(match[1]);
+      }
+      while (importHints.length > 0) {
+        var m = importHints.pop();
+        if (m in files) continue;
+        if (editor.getFiles().indexOf(fileKey(m)) !== -1) {
+          files[m] = window.localStorage[fileKey(m)];
+          reloop = true;
+        } else if (m.startsWith('./') && editor.getFiles().indexOf(fileKey(m.slice(2))) !== -1) {
+          files[m] = window.localStorage[fileKey(m.slice(2))];
+          reloop = true;
+        } else if (m in cachedRemoteFiles) {
+          files[m] = cachedRemoteFiles[m];
+          reloop = true;
+        } else if (githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m)) {
+          handleGithubCall(function(result) {
+            if ('content' in result)
+            {
+              var content = Base64.decode(result.content);
+              cachedRemoteFiles[m] = content;
+              files[m] = content;
+              gatherImports(files, importHints, cb);
+            }
+            else
+              cb(null, "Unable to import \"" + m + "\"");
+          }).fail(function(){
+            cb(null, "Unable to import \"" + m + "\"");
+          });
+          return;
+        } else {
+          cb(null, "Unable to import \"" + m + "\"");
+          return;
+        }
+      }
+    } while (reloop);
+    cb(JSON.stringify({'sources':files}));
+  }
+}
+
+module.exports = Compiler

From 9c4a31afe0a711d3e57a35caa3a72e2a85abfa33 Mon Sep 17 00:00:00 2001
From: Dave Hoover 
Date: Mon, 30 May 2016 08:20:50 -0500
Subject: [PATCH 06/14] pulling some widely-used functions/data into
 app/utils.js

---
 src/app.js                 | 47 +++++++++++++++-----------------------
 src/app/compiler.js        | 15 ++++++------
 src/app/editor.js          |  8 ++++---
 src/app/storage-handler.js |  6 +++--
 src/app/utils.js           | 19 +++++++++++++++
 5 files changed, 55 insertions(+), 40 deletions(-)
 create mode 100644 src/app/utils.js

diff --git a/src/app.js b/src/app.js
index 6c01020d7b..a959ac1160 100644
--- a/src/app.js
+++ b/src/app.js
@@ -2,6 +2,7 @@ var $ = require('jquery');
 var UniversalDApp = require('./universal-dapp.js');
 var web3 = require('./web3-adapter.js');
 
+var utils = require('./app/utils');
 var queryParams = require('./app/query-params');
 var gistHandler = require('./app/gist-handler');
 
@@ -36,7 +37,7 @@ var run = function() {
 
 	function loadFiles(files) {
 		for (var f in files) {
-			var key = fileKey(f);
+			var key = utils.fileKey(f);
 			var content = files[f].content;
 			if (key in window.localStorage && window.localStorage[key] != content) {
 				var count = '';
@@ -46,7 +47,7 @@ var run = function() {
 			}
 			window.localStorage[key] = content;
 		}
-		editor.setCacheFile(fileKey(Object.keys(files)[0]));
+		editor.setCacheFile(utils.fileKey(Object.keys(files)[0]));
 		updateFiles();
 	}
 
@@ -75,16 +76,14 @@ var run = function() {
 
 	// ----------------- storage --------------------
 
-	var SOL_CACHE_FILE_PREFIX = 'sol-cache-file-';
-
-	var storageHandler = new StorageHandler(updateFiles, SOL_CACHE_FILE_PREFIX);
+	var storageHandler = new StorageHandler(updateFiles);
 	window.syncStorage = storageHandler.sync;
 	storageHandler.sync();
 
 
 	// ----------------- editor ----------------------
 
-	var editor = new Editor(loadingFromGist, SOL_CACHE_FILE_PREFIX);
+	var editor = new Editor(loadingFromGist);
 
 
 	// ----------------- tabbed menu -------------------
@@ -150,7 +149,7 @@ var run = function() {
 		var filesArr = editor.getFiles();
 
 		for (var f in filesArr) {
-			files[fileNameFromKey(filesArr[f])] = {
+			files[utils.fileNameFromKey(filesArr[f])] = {
 				content: localStorage[filesArr[f]]
 			};
 		}
@@ -228,10 +227,10 @@ var run = function() {
 			$fileNameInputEl.off('keyup');
 
 			if (newName !== originalName && confirm("Are you sure you want to rename: " + originalName + " to " + newName + '?')) {
-				var content = window.localStorage.getItem( fileKey(originalName) );
-				window.localStorage[fileKey( newName )] = content;
-				window.localStorage.removeItem( fileKey( originalName) );
-				editor.setCacheFile(fileKey( newName ));
+				var content = window.localStorage.getItem( utils.fileKey(originalName) );
+				window.localStorage[utils.fileKey( newName )] = content;
+				window.localStorage.removeItem( utils.fileKey( originalName) );
+				editor.setCacheFile(utils.fileKey( newName ));
 			}
 
 			updateFiles();
@@ -246,8 +245,8 @@ var run = function() {
 		var name = $(this).parent().find('.name').text();
 
 		if (confirm("Are you sure you want to remove: " + name + " from local storage?")) {
-			window.localStorage.removeItem( fileKey( name ) );
-			editor.setNextFile(fileKey(name));
+			window.localStorage.removeItem( utils.fileKey( name ) );
+			editor.setNextFile(utils.fileKey(name));
 			updateFiles();
 		}
 		return false;
@@ -255,13 +254,13 @@ var run = function() {
 
 	function showFileHandler(ev) {
 		ev.preventDefault();
-		editor.setCacheFile(fileKey( $(this).find('.name').text() ));
+		editor.setCacheFile(utils.fileKey( $(this).find('.name').text() ));
 		updateFiles();
 		return false;
 	}
 
 	function fileTabFromKey(key) {
-		var name = fileNameFromKey(key);
+		var name = utils.fileNameFromKey(key);
 		return $('#files .file').filter(function(){ return $(this).find('.name').text() == name; });
 	}
 
@@ -287,18 +286,10 @@ var run = function() {
 	}
 
 	function fileTabTemplate(key) {
-		var name = fileNameFromKey(key);
+		var name = utils.fileNameFromKey(key);
 		return $('
  • '+name+'
  • '); } - function fileKey( name ) { - return SOL_CACHE_FILE_PREFIX + name; - } - - function fileNameFromKey(key) { - return key.replace( SOL_CACHE_FILE_PREFIX, '' ); - } - $filesWrapper = $('.files-wrapper'); $scrollerRight = $('.scroller-right'); $scrollerLeft = $('.scroller-left'); @@ -469,7 +460,7 @@ var run = function() { var errFile = err[1]; var errLine = parseInt(err[2], 10) - 1; var errCol = err[4] ? parseInt(err[4], 10) : 0; - if (errFile == '' || errFile == fileNameFromKey(editor.getCacheFile())) { + if (errFile == '' || errFile == utils.fileNameFromKey(editor.getCacheFile())) { compiler.addAnnotation({ row: errLine, column: errCol, @@ -478,9 +469,9 @@ var run = function() { }); } $error.click(function(ev){ - if (errFile != '' && errFile != fileNameFromKey(editor.getCacheFile()) && editor.getFiles().indexOf(fileKey(errFile)) !== -1) { + if (errFile != '' && errFile != utils.fileNameFromKey(editor.getCacheFile()) && editor.getFiles().indexOf(utils.fileKey(errFile)) !== -1) { // Switch to file - editor.setCacheFile(fileKey(errFile)); + editor.setCacheFile(utils.fileKey(errFile)); updateFiles(); //@TODO could show some error icon in files with errors } @@ -674,7 +665,7 @@ var run = function() { return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path, cb); } - var compiler = new Compiler(editor, renderContracts, renderError, errortype, fileNameFromKey, fileKey, handleGithubCall, $('#output'), function() { return hidingRHP; }); + var compiler = new Compiler(editor, renderContracts, renderError, errortype, handleGithubCall, $('#output'), function() { return hidingRHP; }); function setVersionText(text) { $('#version').text(text); diff --git a/src/app/compiler.js b/src/app/compiler.js index fd9ce09e96..ea98cfa6b4 100644 --- a/src/app/compiler.js +++ b/src/app/compiler.js @@ -1,6 +1,7 @@ var queryParams = require('./query-params'); +var utils = require('./utils'); -function Compiler(editor, renderContracts, renderError, errortype, fileNameFromKey, fileKey, handleGithubCall, outputField, hidingRHP) { +function Compiler(editor, renderContracts, renderError, errortype, handleGithubCall, outputField, hidingRHP) { var compileJSON; var compilerAcceptsMultipleFiles; @@ -36,7 +37,7 @@ function Compiler(editor, renderContracts, renderError, errortype, fileNameFromK window.localStorage.setItem(editor.getCacheFile(), input); var files = {}; - files[fileNameFromKey(editor.getCacheFile())] = input; + files[utils.fileNameFromKey(editor.getCacheFile())] = input; gatherImports(files, missingInputs, function(input, error) { outputField.empty(); if (input === null) { @@ -151,7 +152,7 @@ function Compiler(editor, renderContracts, renderError, errortype, fileNameFromK importHints = importHints || []; if (!compilerAcceptsMultipleFiles) { - cb(files[fileNameFromKey(editor.getCacheFile())]); + cb(files[utils.fileNameFromKey(editor.getCacheFile())]); return; } var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g; @@ -166,11 +167,11 @@ function Compiler(editor, renderContracts, renderError, errortype, fileNameFromK while (importHints.length > 0) { var m = importHints.pop(); if (m in files) continue; - if (editor.getFiles().indexOf(fileKey(m)) !== -1) { - files[m] = window.localStorage[fileKey(m)]; + if (editor.getFiles().indexOf(utils.fileKey(m)) !== -1) { + files[m] = window.localStorage[utils.fileKey(m)]; reloop = true; - } else if (m.startsWith('./') && editor.getFiles().indexOf(fileKey(m.slice(2))) !== -1) { - files[m] = window.localStorage[fileKey(m.slice(2))]; + } else if (m.startsWith('./') && editor.getFiles().indexOf(utils.fileKey(m.slice(2))) !== -1) { + files[m] = window.localStorage[utils.fileKey(m.slice(2))]; reloop = true; } else if (m in cachedRemoteFiles) { files[m] = cachedRemoteFiles[m]; diff --git a/src/app/editor.js b/src/app/editor.js index f71416920a..01d9c0f1b5 100644 --- a/src/app/editor.js +++ b/src/app/editor.js @@ -1,7 +1,9 @@ +var utils = require('./utils'); + var ace = require('brace'); require('../mode-solidity.js'); -function Editor(loadingFromGist, SOL_CACHE_FILE_PREFIX) { +function Editor(loadingFromGist) { this.newFile = function() { untitledCount = ''; @@ -38,7 +40,7 @@ function Editor(loadingFromGist, SOL_CACHE_FILE_PREFIX) { this.getFiles = function() { var files = []; for (var f in localStorage ) { - if (f.indexOf( SOL_CACHE_FILE_PREFIX, 0 ) === 0) { + if (f.indexOf( utils.getCacheFilePrefix(), 0 ) === 0) { files.push(f); if (!sessions[f]) sessions[f] = newEditorSession(f); } @@ -116,7 +118,7 @@ function Editor(loadingFromGist, SOL_CACHE_FILE_PREFIX) { editor.resize(true); } - var SOL_CACHE_UNTITLED = SOL_CACHE_FILE_PREFIX + 'Untitled'; + var SOL_CACHE_UNTITLED = utils.getCacheFilePrefix() + 'Untitled'; var SOL_CACHE_FILE = null; var editor = ace.edit("input"); diff --git a/src/app/storage-handler.js b/src/app/storage-handler.js index c68c340716..de78d205f9 100644 --- a/src/app/storage-handler.js +++ b/src/app/storage-handler.js @@ -1,4 +1,6 @@ -function StorageHandler(updateFiles, SOL_CACHE_FILE_PREFIX) { +var utils = require('./utils'); + +function StorageHandler(updateFiles) { this.sync = function() { @@ -30,7 +32,7 @@ function StorageHandler(updateFiles, SOL_CACHE_FILE_PREFIX) { for (var y in window.localStorage) { console.log("checking", y); obj[y] = window.localStorage.getItem(y); - if (y.indexOf(SOL_CACHE_FILE_PREFIX) !== 0) continue; + if (y.indexOf(utils.getCacheFilePrefix()) !== 0) continue; count++; check(y); } diff --git a/src/app/utils.js b/src/app/utils.js new file mode 100644 index 0000000000..2ece9ac557 --- /dev/null +++ b/src/app/utils.js @@ -0,0 +1,19 @@ +var SOL_CACHE_FILE_PREFIX = 'sol-cache-file-'; + +function getCacheFilePrefix() { + return SOL_CACHE_FILE_PREFIX; +} + +function fileKey( name ) { + return getCacheFilePrefix() + name; +} + +function fileNameFromKey(key) { + return key.replace( getCacheFilePrefix(), '' ); +} + +module.exports = { + getCacheFilePrefix: getCacheFilePrefix, + fileKey: fileKey, + fileNameFromKey: fileNameFromKey +}; From 5474e89cb7ec6d4e96e3ad408adf14c415c8ce6a Mon Sep 17 00:00:00 2001 From: Dave Hoover Date: Mon, 30 May 2016 08:50:57 -0500 Subject: [PATCH 07/14] A little more compiler cleanup --- src/app.js | 10 ++-------- src/app/compiler.js | 8 +++++--- src/app/utils.js | 7 ++++++- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/app.js b/src/app.js index a959ac1160..ce38efc492 100644 --- a/src/app.js +++ b/src/app.js @@ -20,8 +20,6 @@ window.addEventListener("message", function(ev) { } }, false); -var Base64 = require('js-base64').Base64; - var run = function() { // ------------------ query params (hash) ---------------- @@ -446,12 +444,8 @@ var run = function() { // ----------------- compiler output renderer ---------------------- var detailsOpen = {}; - function errortype(message) { - return message.match(/^.*:[0-9]*:[0-9]* Warning: /) ? 'warning' : 'error'; - } - var renderError = function(message) { - var type = errortype(message); + var type = utils.errortype(message); var $pre = $("
    ").text(message);
     		var $error = $('
    ').prepend($pre); $('#output').append( $error ); @@ -665,7 +659,7 @@ var run = function() { return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path, cb); } - var compiler = new Compiler(editor, renderContracts, renderError, errortype, handleGithubCall, $('#output'), function() { return hidingRHP; }); + var compiler = new Compiler(editor, renderContracts, renderError, handleGithubCall, $('#output'), function() { return hidingRHP; }); function setVersionText(text) { $('#version').text(text); diff --git a/src/app/compiler.js b/src/app/compiler.js index ea98cfa6b4..0435de666b 100644 --- a/src/app/compiler.js +++ b/src/app/compiler.js @@ -1,7 +1,9 @@ var queryParams = require('./query-params'); var utils = require('./utils'); -function Compiler(editor, renderContracts, renderError, errortype, handleGithubCall, outputField, hidingRHP) { +var Base64 = require('js-base64').Base64; + +function Compiler(editor, renderContracts, renderError, handleGithubCall, outputField, hidingRHP) { var compileJSON; var compilerAcceptsMultipleFiles; @@ -108,12 +110,12 @@ function Compiler(editor, renderContracts, renderError, errortype, handleGithubC if (data['error'] !== undefined) { renderError(data['error']); - if (errortype(data['error']) !== 'warning') noFatalErrors = false; + if (utils.errortype(data['error']) !== 'warning') noFatalErrors = false; } if (data['errors'] != undefined) { data['errors'].forEach(function(err) { renderError(err); - if (errortype(err) !== 'warning') noFatalErrors = false; + if (utils.errortype(err) !== 'warning') noFatalErrors = false; }); } diff --git a/src/app/utils.js b/src/app/utils.js index 2ece9ac557..732935272d 100644 --- a/src/app/utils.js +++ b/src/app/utils.js @@ -12,8 +12,13 @@ function fileNameFromKey(key) { return key.replace( getCacheFilePrefix(), '' ); } +function errortype(message) { + return message.match(/^.*:[0-9]*:[0-9]* Warning: /) ? 'warning' : 'error'; +} + module.exports = { getCacheFilePrefix: getCacheFilePrefix, fileKey: fileKey, - fileNameFromKey: fileNameFromKey + fileNameFromKey: fileNameFromKey, + errortype: errortype }; From 0a37e6a3fbcd8be1e09affa6b860bb6d03143941 Mon Sep 17 00:00:00 2001 From: Dave Hoover Date: Mon, 30 May 2016 12:08:41 -0500 Subject: [PATCH 08/14] Pulling some utility calls into editor in order to simplify app.js --- src/app.js | 18 +++++++++--------- src/app/editor.js | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/app.js b/src/app.js index ce38efc492..b6ccac3a44 100644 --- a/src/app.js +++ b/src/app.js @@ -45,7 +45,7 @@ var run = function() { } window.localStorage[key] = content; } - editor.setCacheFile(utils.fileKey(Object.keys(files)[0])); + editor.setCacheFile(Object.keys(files)[0]); updateFiles(); } @@ -228,7 +228,7 @@ var run = function() { var content = window.localStorage.getItem( utils.fileKey(originalName) ); window.localStorage[utils.fileKey( newName )] = content; window.localStorage.removeItem( utils.fileKey( originalName) ); - editor.setCacheFile(utils.fileKey( newName )); + editor.setCacheFile(newName); } updateFiles(); @@ -252,13 +252,13 @@ var run = function() { function showFileHandler(ev) { ev.preventDefault(); - editor.setCacheFile(utils.fileKey( $(this).find('.name').text() )); + editor.setCacheFile($(this).find('.name').text()); updateFiles(); return false; } - function fileTabFromKey(key) { - var name = utils.fileNameFromKey(key); + function fileTabFromKey() { + var name = editor.getCacheFile(); return $('#files .file').filter(function(){ return $(this).find('.name').text() == name; }); } @@ -274,7 +274,7 @@ var run = function() { } if (editor.cacheFileIsPresent()) { - var active = fileTabFromKey(editor.getCacheFile()); + var active = fileTabFromKey(); active.addClass('active'); editor.resetSession(); } @@ -454,7 +454,7 @@ var run = function() { var errFile = err[1]; var errLine = parseInt(err[2], 10) - 1; var errCol = err[4] ? parseInt(err[4], 10) : 0; - if (errFile == '' || errFile == utils.fileNameFromKey(editor.getCacheFile())) { + if (errFile == '' || errFile == editor.getCacheFile()) { compiler.addAnnotation({ row: errLine, column: errCol, @@ -463,9 +463,9 @@ var run = function() { }); } $error.click(function(ev){ - if (errFile != '' && errFile != utils.fileNameFromKey(editor.getCacheFile()) && editor.getFiles().indexOf(utils.fileKey(errFile)) !== -1) { + if (errFile != '' && errFile != editor.getCacheFile() && editor.getFiles().indexOf(utils.fileKey(errFile)) !== -1) { // Switch to file - editor.setCacheFile(utils.fileKey(errFile)); + editor.setCacheFile(errFile); updateFiles(); //@TODO could show some error icon in files with errors } diff --git a/src/app/editor.js b/src/app/editor.js index 01d9c0f1b5..d19a102c6e 100644 --- a/src/app/editor.js +++ b/src/app/editor.js @@ -15,11 +15,11 @@ function Editor(loadingFromGist) { }; this.setCacheFile = function(cacheFile) { - SOL_CACHE_FILE = cacheFile; + SOL_CACHE_FILE = utils.fileKey(cacheFile); }; this.getCacheFile = function() { - return SOL_CACHE_FILE; + return utils.fileNameFromKey(SOL_CACHE_FILE); }; this.cacheFileIsPresent = function() { From fe1bc2211712193f3bda50e817177d98478676e1 Mon Sep 17 00:00:00 2001 From: Dave Hoover Date: Mon, 30 May 2016 16:36:22 -0500 Subject: [PATCH 09/14] Extracted renderer code into a child of compiler --- src/app.js | 210 +--------------------------------------- src/app/compiler.js | 22 +++-- src/app/editor.js | 4 + src/app/renderer.js | 230 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 248 insertions(+), 218 deletions(-) create mode 100644 src/app/renderer.js diff --git a/src/app.js b/src/app.js index b6ccac3a44..b1d68d3e55 100644 --- a/src/app.js +++ b/src/app.js @@ -1,5 +1,4 @@ var $ = require('jquery'); -var UniversalDApp = require('./universal-dapp.js'); var web3 = require('./web3-adapter.js'); var utils = require('./app/utils'); @@ -427,6 +426,7 @@ var run = function() { if (!hidingRHP) compiler.compile(); }); + function getHidingRHP() { return hidingRHP; } // ----------------- editor resize --------------- @@ -442,215 +442,9 @@ var run = function() { // ----------------- compiler output renderer ---------------------- - var detailsOpen = {}; - - var renderError = function(message) { - var type = utils.errortype(message); - var $pre = $("
    ").text(message);
    -		var $error = $('
    ').prepend($pre); - $('#output').append( $error ); - var err = message.match(/^([^:]*):([0-9]*):(([0-9]*):)? /); - if (err) { - var errFile = err[1]; - var errLine = parseInt(err[2], 10) - 1; - var errCol = err[4] ? parseInt(err[4], 10) : 0; - if (errFile == '' || errFile == editor.getCacheFile()) { - compiler.addAnnotation({ - row: errLine, - column: errCol, - text: message, - type: type - }); - } - $error.click(function(ev){ - if (errFile != '' && errFile != editor.getCacheFile() && editor.getFiles().indexOf(utils.fileKey(errFile)) !== -1) { - // Switch to file - editor.setCacheFile(errFile); - updateFiles(); - //@TODO could show some error icon in files with errors - } - editor.handleErrorClick(errLine, errCol); - }); - $error.find('.close').click(function(ev){ - ev.preventDefault(); - $error.remove(); - return false; - }); - } - }; - - var gethDeploy = function(contractName, jsonInterface, bytecode){ - var code = ""; - var funABI = getConstructorInterface($.parseJSON(jsonInterface)); - - $.each(funABI.inputs, function(i, inp) { - code += "var " + inp.name + " = /* var of type " + inp.type + " here */ ;\n"; - }); - - code += "var " + contractName + "Contract = web3.eth.contract(" + jsonInterface.replace("\n","") + ");" - +"\nvar " + contractName + " = " + contractName + "Contract.new("; - - $.each(funABI.inputs, function(i, inp) { - code += "\n " + inp.name + ","; - }); - - code += "\n {"+ - "\n from: web3.eth.accounts[0], "+ - "\n data: '"+bytecode+"', "+ - "\n gas: 3000000"+ - "\n }, function(e, contract){"+ - "\n console.log(e, contract);"+ - "\n if (typeof contract.address != 'undefined') {"+ - "\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" + - "\n }" + - "\n })"; - - - return code; - }; - - var combined = function(contractName, jsonInterface, bytecode){ - return JSON.stringify([{name: contractName, interface: jsonInterface, bytecode: bytecode}]); - }; - - var renderContracts = function(data, source) { - var udappContracts = []; - for (var contractName in data.contracts) { - var contract = data.contracts[contractName]; - udappContracts.push({ - name: contractName, - interface: contract['interface'], - bytecode: contract.bytecode - }); - } - var dapp = new UniversalDApp(udappContracts, { - vm: executionContext === 'vm', - removable: false, - getAddress: function(){ return $('#txorigin').val(); }, - getValue: function(){ - var comp = $('#value').val().split(' '); - return web3.toWei(comp[0], comp.slice(1).join(' ')); - }, - removable_instances: true, - renderOutputModifier: function(contractName, $contractOutput) { - var contract = data.contracts[contractName]; - return $contractOutput - .append(textRow('Bytecode', contract.bytecode)) - .append(textRow('Interface', contract['interface'])) - .append(textRow('Web3 deploy', gethDeploy(contractName.toLowerCase(),contract['interface'],contract.bytecode), 'deploy')) - .append(textRow('uDApp', combined(contractName,contract['interface'],contract.bytecode), 'deploy')) - .append(getDetails(contract, source, contractName)); - }}); - var $contractOutput = dapp.render(); - - $txOrigin = $('#txorigin'); - function renderAccounts(err, accounts) { - if (err) - renderError(err.message); - if (accounts && accounts[0]){ - $txOrigin.empty(); - for( var a in accounts) { $txOrigin.append($('