@ -1,4 +1,4 @@
/* global alert, confirm, prompt, Option, Worker, chrome */
/* global alert, confirm, prompt, FileReader, Option, Worker, chrome */
'use strict'
'use strict'
var $ = require ( 'jquery' )
var $ = require ( 'jquery' )
@ -10,6 +10,7 @@ var GistHandler = require('./app/gist-handler')
var gistHandler = new GistHandler ( )
var gistHandler = new GistHandler ( )
var Storage = require ( './app/storage' )
var Storage = require ( './app/storage' )
var Files = require ( './app/files' )
var Config = require ( './app/config' )
var Config = require ( './app/config' )
var Editor = require ( './app/editor' )
var Editor = require ( './app/editor' )
var Renderer = require ( './app/renderer' )
var Renderer = require ( './app/renderer' )
@ -22,6 +23,8 @@ var EventManager = require('./lib/eventManager')
var StaticAnalysis = require ( './app/staticanalysis/staticAnalysisView' )
var StaticAnalysis = require ( './app/staticanalysis/staticAnalysisView' )
var OffsetToLineColumnConverter = require ( './lib/offsetToLineColumnConverter' )
var OffsetToLineColumnConverter = require ( './lib/offsetToLineColumnConverter' )
var examples = require ( './app/example-contracts' )
// The event listener needs to be registered as early as possible, because the
// The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event.
// parent will send the message upon the "load" event.
var filesToLoad = null
var filesToLoad = null
@ -40,16 +43,28 @@ var run = function () {
var self = this
var self = this
this . event = new EventManager ( )
this . event = new EventManager ( )
var storage = new Storage ( )
var storage = new Storage ( )
var files = new Files ( storage )
var config = new Config ( storage )
var config = new Config ( storage )
var currentFile
function packageFiles ( ) {
return files . list ( ) . map ( function ( path ) { return { content : files . get ( path ) } } )
}
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)
// Add files received from remote instance (i.e. another browser-solidity)
function loadFiles ( files ) {
function loadFiles ( files ) {
for ( var f in files ) {
for ( var f in files ) {
storage . loadFile ( f , files [ f ] . content )
files . set ( createNonClashingNam e( f ) , files [ f ] . content )
}
}
// Set the first file as current tab
switchToNextFile ( )
editor . setCacheFile ( Object . keys ( files ) [ 0 ] )
updateFiles ( )
}
}
// Replace early callback with instant response
// Replace early callback with instant response
@ -87,6 +102,11 @@ var run = function () {
} )
} )
} )
} )
// insert ballot contract if there are no files available
if ( ! loadingFromGist && ( files . list ( ) . length === 0 ) ) {
files . set ( examples . ballot . name , examples . ballot . content )
}
// ----------------- Chrome cloud storage sync --------------------
// ----------------- Chrome cloud storage sync --------------------
function chromeCloudSync ( ) {
function chromeCloudSync ( ) {
@ -131,7 +151,7 @@ var run = function () {
// ----------------- editor ----------------------
// ----------------- editor ----------------------
var editor = new Editor ( loadingFromGist , storage )
var editor = new Editor ( )
// ----------------- tabbed menu -------------------
// ----------------- tabbed menu -------------------
$ ( '#options li' ) . click ( function ( ev ) {
$ ( '#options li' ) . click ( function ( ev ) {
@ -155,7 +175,7 @@ var run = function () {
$ ( '#gist' ) . click ( function ( ) {
$ ( '#gist' ) . click ( function ( ) {
if ( confirm ( 'Are you sure you want to publish all your files anonymously as a public gist on github.com?' ) ) {
if ( confirm ( 'Are you sure you want to publish all your files anonymously as a public gist on github.com?' ) ) {
var files = editor . packageFiles ( )
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='
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 ( {
$ . ajax ( {
@ -182,7 +202,7 @@ var run = function () {
if ( target === null ) {
if ( target === null ) {
return
return
}
}
var files = editor . packageFiles ( )
var files = packageFiles ( )
$ ( '<iframe/>' , {
$ ( '<iframe/>' , {
src : target ,
src : target ,
style : 'display:none;' ,
style : 'display:none;' ,
@ -196,12 +216,9 @@ var run = function () {
var FILE _SCROLL _DELTA = 300
var FILE _SCROLL _DELTA = 300
$ ( '.newFile' ) . on ( 'click' , function ( ) {
$ ( '.newFile' ) . on ( 'click' , function ( ) {
editor . newFile ( )
var newName = createNonClashingName ( 'Untitled' )
updateFiles ( )
files . set ( newName , '' )
switchToFile ( newName )
$filesEl . animate ( { left : Math . max ( ( 0 - activeFilePos ( ) + ( FILE _SCROLL _DELTA / 2 ) ) , 0 ) + 'px' } , 'slow' , function ( ) {
reAdjust ( )
} )
} )
} )
// ----------------- file upload -------------
// ----------------- file upload -------------
@ -210,14 +227,15 @@ var run = function () {
var fileList = $ ( 'input.inputFile' ) [ 0 ] . files
var fileList = $ ( 'input.inputFile' ) [ 0 ] . files
for ( var i = 0 ; i < fileList . length ; i ++ ) {
for ( var i = 0 ; i < fileList . length ; i ++ ) {
var name = fileList [ i ] . name
var name = fileList [ i ] . name
if ( ! editor . hasFile ( name ) || confirm ( 'The file ' + name + ' already exists! Would you like to overwrite it?' ) ) {
if ( ! files . exists ( name ) || confirm ( 'The file ' + name + ' already exists! Would you like to overwrite it?' ) ) {
editor . uploadFile ( fileList [ i ] , updateFiles )
var fileReader = new FileReader ( )
fileReader . onload = function ( ev ) {
files . set ( name , ev . target . result )
switchToFile ( name )
}
fileReader . readAsText ( fileList [ i ] )
}
}
}
}
$filesEl . animate ( { left : Math . max ( ( 0 - activeFilePos ( ) + ( FILE _SCROLL _DELTA / 2 ) ) , 0 ) + 'px' } , 'slow' , function ( ) {
reAdjust ( )
} )
} )
} )
// Switch tab
// Switch tab
@ -248,15 +266,14 @@ var run = function () {
$fileNameInputEl . off ( 'keyup' )
$fileNameInputEl . off ( 'keyup' )
if ( newName !== originalName && confirm (
if ( newName !== originalName && confirm (
editor . hasFile ( newName )
files . exists ( newName )
? 'Are you sure you want to overwrite: ' + newName + ' with ' + originalName + '?'
? 'Are you sure you want to overwrite: ' + newName + ' with ' + originalName + '?'
: 'Are you sure you want to rename: ' + originalName + ' to ' + newName + '?' ) ) {
: 'Are you sure you want to rename: ' + originalName + ' to ' + newName + '?' ) ) {
storage . rename ( originalName , newName )
files . rename ( originalName , newName )
editor . renameSession ( originalName , newName )
switchToFile ( newName )
editor . setCacheFile ( new Name)
editor . discard ( original Name)
}
}
updateFiles ( )
return false
return false
}
}
@ -269,10 +286,9 @@ var run = function () {
var name = $ ( this ) . parent ( ) . find ( '.name' ) . text ( )
var name = $ ( this ) . parent ( ) . find ( '.name' ) . text ( )
if ( confirm ( 'Are you sure you want to remove: ' + name + ' from local storage?' ) ) {
if ( confirm ( 'Are you sure you want to remove: ' + name + ' from local storage?' ) ) {
storage . remove ( name )
files . remove ( name )
editor . removeSession ( name )
switchToNextFile ( )
editor . setNextFile ( name )
editor . discard ( name )
updateFiles ( )
}
}
return false
return false
} )
} )
@ -280,32 +296,46 @@ var run = function () {
editor . event . register ( 'sessionSwitched' , updateFiles )
editor . event . register ( 'sessionSwitched' , updateFiles )
function switchToFile ( file ) {
function switchToFile ( file ) {
editor . setCacheFile ( file )
currentFile = file
updateFiles ( )
if ( files . isReadOnly ( file ) ) {
editor . openReadOnly ( file , files . get ( file ) )
} else {
editor . open ( file , files . get ( file ) )
}
}
function switchToNextFile ( ) {
switchToFile ( Object . keys ( files . list ( ) ) [ 0 ] )
}
}
switchToNextFile ( )
// Synchronise tab list with file names known to the editor
// Synchronise tab list with file names known to the editor
function updateFiles ( ) {
function updateFiles ( ) {
var $filesEl = $ ( '#files' )
var $filesEl = $ ( '#files' )
var files = editor . getFiles ( )
var fileNames = Object . keys ( files . list ( ) )
$filesEl . find ( '.file' ) . remove ( )
$filesEl . find ( '.file' ) . remove ( )
$ ( '#output' ) . empty ( )
$ ( '#output' ) . empty ( )
for ( var f in files ) {
for ( var f in fileName s ) {
var name = files [ f ]
var name = fileName s [ f ]
$filesEl . append ( $ ( '<li class="file"><span class="name">' + name + '</span><span class="remove"><i class="fa fa-close"></i></span></li>' ) )
$filesEl . append ( $ ( '<li class="file"><span class="name">' + name + '</span><span class="remove"><i class="fa fa-close"></i></span></li>' ) )
}
}
if ( editor . cacheFileIsPresent ( ) ) {
var currentFileOpen = ! ! currentFile
var currentFileName = editor . getCacheFile ( )
var active = $ ( '#files .file' ) . filter ( function ( ) { return $ ( this ) . find ( '.name' ) . text ( ) === currentFileName } )
if ( currentFileOpen ) {
var active = $ ( '#files .file' ) . filter ( function ( ) { return $ ( this ) . find ( '.name' ) . text ( ) === currentFile } )
active . addClass ( 'active' )
active . addClass ( 'active' )
editor . resetSession ( )
}
}
$ ( '#input' ) . toggle ( editor . cacheFileIsPresent ( ) )
$ ( '#input' ) . toggle ( currentFileOpen )
$ ( '#output' ) . toggle ( editor . cacheFileIsPresent ( ) )
$ ( '#output' ) . toggle ( currentFileOpen )
$filesEl . animate ( { left : Math . max ( ( 0 - activeFilePos ( ) + ( FILE _SCROLL _DELTA / 2 ) ) , 0 ) + 'px' } , 'slow' , function ( ) {
reAdjust ( )
reAdjust ( )
} )
}
}
var $filesWrapper = $ ( '.files-wrapper' )
var $filesWrapper = $ ( '.files-wrapper' )
@ -368,8 +398,6 @@ var run = function () {
} )
} )
} )
} )
updateFiles ( )
// ----------------- resizeable ui ---------------
// ----------------- resizeable ui ---------------
var EDITOR _WINDOW _SIZE = 'editorWindowSize'
var EDITOR _WINDOW _SIZE = 'editorWindowSize'
@ -470,8 +498,8 @@ var run = function () {
function handleImportCall ( url , cb ) {
function handleImportCall ( url , cb ) {
var githubMatch
var githubMatch
if ( editor . hasFile ( url ) ) {
if ( files . exists ( url ) ) {
cb ( null , editor . getFile ( url ) )
cb ( null , files . get ( url ) )
} else if ( url in cachedRemoteFiles ) {
} else if ( url in cachedRemoteFiles ) {
cb ( null , cachedRemoteFiles [ url ] )
cb ( null , cachedRemoteFiles [ url ] )
} else if ( ( githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^/]*\/[^/]*)\/(.*)/ . exec ( url ) ) ) {
} else if ( ( githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^/]*\/[^/]*)\/(.*)/ . exec ( url ) ) ) {
@ -506,9 +534,8 @@ var run = function () {
this . statementMarker = null
this . statementMarker = null
this . fullLineMarker = null
this . fullLineMarker = null
if ( lineColumnPos ) {
if ( lineColumnPos ) {
var name = editor . getCacheFile ( ) // current opened tab
var source = compiler . lastCompilationResult . data . sourceList [ location . file ] // auto switch to that tab
var source = compiler . lastCompilationResult . data . sourceList [ location . file ] // auto switch to that tab
if ( nam e !== source ) {
if ( currentFil e !== source ) {
switchToFile ( source )
switchToFile ( source )
}
}
this . statementMarker = editor . addMarker ( lineColumnPos , 'highlightcode' )
this . statementMarker = editor . addMarker ( lineColumnPos , 'highlightcode' )
@ -569,12 +596,12 @@ var run = function () {
var rendererAPI = {
var rendererAPI = {
error : ( file , error ) => {
error : ( file , error ) => {
if ( file === editor . getCacheFile ( ) ) {
if ( file === currentFile ) {
editor . addAnnotation ( error )
editor . addAnnotation ( error )
}
}
} ,
} ,
errorClick : ( errFile , errLine , errCol ) => {
errorClick : ( errFile , errLine , errCol ) => {
if ( errFile !== editor . getCacheFile ( ) && editor . hasFile ( errFile ) ) {
if ( errFile !== currentFile && files . exists ( errFile ) ) {
switchToFile ( errFile )
switchToFile ( errFile )
}
}
editor . gotoLine ( errLine , errCol )
editor . gotoLine ( errLine , errCol )
@ -623,9 +650,9 @@ var run = function () {
function runCompiler ( ) {
function runCompiler ( ) {
var files = { }
var files = { }
var target = editor . getCacheFile ( )
var target = currentFile
files [ target ] = editor . getValue ( )
files [ target ] = editor . get ( currentFile )
compiler . compile ( files , target )
compiler . compile ( files , target )
}
}
@ -635,7 +662,7 @@ var run = function () {
var saveTimeout = null
var saveTimeout = null
function editorOnChange ( ) {
function editorOnChange ( ) {
var input = editor . getValue ( )
var input = editor . get ( currentFile )
// if there's no change, don't do anything
// if there's no change, don't do anything
if ( input === previousInput ) {
if ( input === previousInput ) {
@ -649,8 +676,8 @@ var run = function () {
window . clearTimeout ( saveTimeout )
window . clearTimeout ( saveTimeout )
}
}
saveTimeout = window . setTimeout ( function ( ) {
saveTimeout = window . setTimeout ( function ( ) {
var input = editor . getValue ( )
var input = editor . get ( currentFile )
editor . setCacheFileContent ( input )
files . set ( currentFile , input )
} , 5000 )
} , 5000 )
// special case: there's nothing else to do
// special case: there's nothing else to do