parent
8914952eda
commit
2126391e3c
@ -1,10 +1,10 @@ |
||||
/* global chrome */ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
chrome.browserAction.onClicked.addListener(function (tab) { |
||||
chrome.storage.sync.set({ 'chrome-app-sync': true }); |
||||
chrome.storage.sync.set({ 'chrome-app-sync': true }) |
||||
|
||||
chrome.tabs.create({ 'url': chrome.extension.getURL('index.html') }, function (tab) { |
||||
// tab opened
|
||||
}); |
||||
}); |
||||
}) |
||||
}) |
||||
|
@ -1,56 +1,56 @@ |
||||
var fs = require('fs'); |
||||
var solc = require('solc/wrapper'); |
||||
var soljson = require('../soljson'); |
||||
var compiler = solc(soljson); |
||||
var inputs = require('../test-browser/mockcompiler/requests.js'); |
||||
var compilationResult = gatherCompilationResults(inputs); |
||||
replaceSolCompiler(compilationResult); |
||||
var fs = require('fs') |
||||
var solc = require('solc/wrapper') |
||||
var soljson = require('../soljson') |
||||
var compiler = solc(soljson) |
||||
var inputs = require('../test-browser/mockcompiler/requests.js') |
||||
var compilationResult = gatherCompilationResults(inputs) |
||||
replaceSolCompiler(compilationResult) |
||||
|
||||
function gatherCompilationResults (sol) { |
||||
var compilationResult = {}; |
||||
var compilationResult = {} |
||||
for (var k in sol) { |
||||
var item = sol[k]; |
||||
var result = compile(item, 1); |
||||
compilationResult[result.key] = result; |
||||
result = compile(item, 0); |
||||
compilationResult[result.key] = result; |
||||
var item = sol[k] |
||||
var result = compile(item, 1) |
||||
compilationResult[result.key] = result |
||||
result = compile(item, 0) |
||||
compilationResult[result.key] = result |
||||
} |
||||
return compilationResult; |
||||
return compilationResult |
||||
} |
||||
|
||||
function compile (source, optimization) { |
||||
var missingInputs = []; |
||||
var missingInputs = [] |
||||
var result = compiler.compile(source, optimization, function (path) { |
||||
missingInputs.push(path); |
||||
}); |
||||
var key = optimization.toString(); |
||||
missingInputs.push(path) |
||||
}) |
||||
var key = optimization.toString() |
||||
for (var k in source.sources) { |
||||
key += k + source.sources[k]; |
||||
key += k + source.sources[k] |
||||
} |
||||
key = key.replace(/(\t)|(\n)|( )/g, ''); |
||||
key = key.replace(/(\t)|(\n)|( )/g, '') |
||||
return { |
||||
key: key, |
||||
source: source, |
||||
optimization: optimization, |
||||
missingInputs: missingInputs, |
||||
result: result |
||||
}; |
||||
} |
||||
} |
||||
|
||||
function replaceSolCompiler (results) { |
||||
fs.readFile('./test-browser/mockcompiler/compiler.js', 'utf8', function (error, data) { |
||||
if (error) { |
||||
console.log(error); |
||||
process.exit(1); |
||||
return; |
||||
console.log(error) |
||||
process.exit(1) |
||||
return |
||||
} |
||||
data = data + '\n\nvar mockData = ' + JSON.stringify(results) + ';\n'; |
||||
data = data + '\n\nvar mockData = ' + JSON.stringify(results) + ';\n' |
||||
fs.writeFile('./soljson.js', data, 'utf8', function (error) { |
||||
if (error) { |
||||
console.log(error); |
||||
process.exit(1); |
||||
return; |
||||
console.log(error) |
||||
process.exit(1) |
||||
return |
||||
} |
||||
}); |
||||
}); |
||||
}) |
||||
}) |
||||
} |
||||
|
@ -1,46 +1,46 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var solc = require('solc/wrapper'); |
||||
var solc = require('solc/wrapper') |
||||
|
||||
var compileJSON = function () { return ''; }; |
||||
var missingInputs = []; |
||||
var compileJSON = function () { return '' } |
||||
var missingInputs = [] |
||||
|
||||
module.exports = function (self) { |
||||
self.addEventListener('message', function (e) { |
||||
var data = e.data; |
||||
var data = e.data |
||||
switch (data.cmd) { |
||||
case 'loadVersion': |
||||
delete self.Module; |
||||
delete self.Module |
||||
// NOTE: workaround some browsers?
|
||||
self.Module = undefined; |
||||
self.Module = undefined |
||||
|
||||
compileJSON = null; |
||||
compileJSON = null |
||||
|
||||
self.importScripts(data.data); |
||||
self.importScripts(data.data) |
||||
|
||||
var compiler = solc(self.Module); |
||||
var compiler = solc(self.Module) |
||||
|
||||
compileJSON = function (input, optimize) { |
||||
try { |
||||
return JSON.stringify(compiler.compile(JSON.parse(input), optimize, function (path) { |
||||
missingInputs.push(path); |
||||
return { 'error': 'Deferred import' }; |
||||
})); |
||||
missingInputs.push(path) |
||||
return { 'error': 'Deferred import' } |
||||
})) |
||||
} catch (exception) { |
||||
return JSON.stringify({ error: 'Uncaught JavaScript exception:\n' + exception }); |
||||
return JSON.stringify({ error: 'Uncaught JavaScript exception:\n' + exception }) |
||||
} |
||||
}; |
||||
} |
||||
|
||||
self.postMessage({ |
||||
cmd: 'versionLoaded', |
||||
data: compiler.version(), |
||||
acceptsMultipleFiles: compiler.supportsMulti |
||||
}); |
||||
break; |
||||
}) |
||||
break |
||||
case 'compile': |
||||
missingInputs.length = 0; |
||||
self.postMessage({cmd: 'compiled', job: data.job, data: compileJSON(data.source, data.optimize), missingInputs: missingInputs}); |
||||
break; |
||||
missingInputs.length = 0 |
||||
self.postMessage({cmd: 'compiled', job: data.job, data: compileJSON(data.source, data.optimize), missingInputs: missingInputs}) |
||||
break |
||||
} |
||||
}, false); |
||||
}; |
||||
}, false) |
||||
} |
||||
|
@ -1,265 +1,265 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var solc = require('solc/wrapper'); |
||||
var solc = require('solc/wrapper') |
||||
|
||||
var webworkify = require('webworkify'); |
||||
var utils = require('./utils'); |
||||
var webworkify = require('webworkify') |
||||
var utils = require('./utils') |
||||
|
||||
var EventManager = require('../lib/eventManager'); |
||||
var EventManager = require('../lib/eventManager') |
||||
|
||||
/* |
||||
trigger compilationFinished, compilerLoaded, compilationStarted |
||||
*/ |
||||
function Compiler (editor, handleGithubCall) { |
||||
var self = this; |
||||
this.event = new EventManager(); |
||||
var self = this |
||||
this.event = new EventManager() |
||||
|
||||
var compileJSON; |
||||
var compilerAcceptsMultipleFiles; |
||||
var compileJSON |
||||
var compilerAcceptsMultipleFiles |
||||
|
||||
var cachedRemoteFiles = {}; |
||||
var worker = null; |
||||
var cachedRemoteFiles = {} |
||||
var worker = null |
||||
|
||||
var optimize = false; |
||||
var optimize = false |
||||
|
||||
this.setOptimize = function (_optimize) { |
||||
optimize = _optimize; |
||||
}; |
||||
optimize = _optimize |
||||
} |
||||
|
||||
var compile = function (missingInputs) { |
||||
editor.clearAnnotations(); |
||||
self.event.trigger('compilationStarted', []); |
||||
var input = editor.getValue(); |
||||
editor.setCacheFileContent(input); |
||||
editor.clearAnnotations() |
||||
self.event.trigger('compilationStarted', []) |
||||
var input = editor.getValue() |
||||
editor.setCacheFileContent(input) |
||||
|
||||
var files = {}; |
||||
files[utils.fileNameFromKey(editor.getCacheFile())] = input; |
||||
var files = {} |
||||
files[utils.fileNameFromKey(editor.getCacheFile())] = input |
||||
gatherImports(files, missingInputs, function (input, error) { |
||||
if (input === null) { |
||||
self.lastCompilationResult = null; |
||||
self.event.trigger('compilationFinished', [false, { 'error': error }, files]); |
||||
self.lastCompilationResult = null |
||||
self.event.trigger('compilationFinished', [false, { 'error': error }, files]) |
||||
} else { |
||||
compileJSON(input, optimize ? 1 : 0); |
||||
compileJSON(input, optimize ? 1 : 0) |
||||
} |
||||
}); |
||||
}; |
||||
this.compile = compile; |
||||
}) |
||||
} |
||||
this.compile = compile |
||||
|
||||
function setCompileJSON (_compileJSON) { |
||||
compileJSON = _compileJSON; |
||||
compileJSON = _compileJSON |
||||
} |
||||
this.setCompileJSON = setCompileJSON; // this is exposed for testing
|
||||
this.setCompileJSON = setCompileJSON // this is exposed for testing
|
||||
|
||||
function onCompilerLoaded (version) { |
||||
self.event.trigger('compilerLoaded', [version]); |
||||
self.event.trigger('compilerLoaded', [version]) |
||||
} |
||||
|
||||
function onInternalCompilerLoaded () { |
||||
if (worker === null) { |
||||
var compiler = solc(window.Module); |
||||
var compiler = solc(window.Module) |
||||
|
||||
compilerAcceptsMultipleFiles = compiler.supportsMulti; |
||||
compilerAcceptsMultipleFiles = compiler.supportsMulti |
||||
|
||||
compileJSON = function (source, optimize, cb) { |
||||
var missingInputs = []; |
||||
var missingInputs = [] |
||||
var missingInputsCallback = function (path) { |
||||
missingInputs.push(path); |
||||
return { error: 'Deferred import' }; |
||||
}; |
||||
missingInputs.push(path) |
||||
return { error: 'Deferred import' } |
||||
} |
||||
|
||||
var result; |
||||
var result |
||||
try { |
||||
result = compiler.compile(source, optimize, missingInputsCallback); |
||||
result = compiler.compile(source, optimize, missingInputsCallback) |
||||
} catch (exception) { |
||||
result = { error: 'Uncaught JavaScript exception:\n' + exception }; |
||||
result = { error: 'Uncaught JavaScript exception:\n' + exception } |
||||
} |
||||
|
||||
compilationFinished(result, missingInputs, source); |
||||
}; |
||||
compilationFinished(result, missingInputs, source) |
||||
} |
||||
|
||||
onCompilerLoaded(compiler.version()); |
||||
onCompilerLoaded(compiler.version()) |
||||
} |
||||
} |
||||
|
||||
this.lastCompilationResult = { |
||||
data: null, |
||||
source: null |
||||
}; |
||||
} |
||||
function compilationFinished (data, missingInputs, source) { |
||||
var noFatalErrors = true; // ie warnings are ok
|
||||
var noFatalErrors = true // ie warnings are ok
|
||||
|
||||
function isValidError (error) { |
||||
// The deferred import is not a real error
|
||||
// FIXME: maybe have a better check?
|
||||
if (/Deferred import/.exec(error)) { |
||||
return false; |
||||
return false |
||||
} |
||||
|
||||
return utils.errortype(error) !== 'warning'; |
||||
return utils.errortype(error) !== 'warning' |
||||
} |
||||
|
||||
if (data['error'] !== undefined) { |
||||
// Ignore warnings (and the 'Deferred import' error as those are generated by us as a workaround
|
||||
if (isValidError(data['error'])) { |
||||
noFatalErrors = false; |
||||
noFatalErrors = false |
||||
} |
||||
} |
||||
if (data['errors'] !== undefined) { |
||||
data['errors'].forEach(function (err) { |
||||
// Ignore warnings and the 'Deferred import' error as those are generated by us as a workaround
|
||||
if (isValidError(err)) { |
||||
noFatalErrors = false; |
||||
noFatalErrors = false |
||||
} |
||||
}); |
||||
}) |
||||
} |
||||
|
||||
if (!noFatalErrors) { |
||||
// There are fatal errors - abort here
|
||||
self.lastCompilationResult = null; |
||||
self.event.trigger('compilationFinished', [false, data, source]); |
||||
self.lastCompilationResult = null |
||||
self.event.trigger('compilationFinished', [false, data, source]) |
||||
} else if (missingInputs !== undefined && missingInputs.length > 0) { |
||||
compile(missingInputs); |
||||
compile(missingInputs) |
||||
} else { |
||||
self.lastCompilationResult = { |
||||
data: data, |
||||
source: source |
||||
}; |
||||
self.event.trigger('compilationFinished', [true, data, source]); |
||||
} |
||||
self.event.trigger('compilationFinished', [true, data, source]) |
||||
} |
||||
} |
||||
|
||||
this.loadVersion = function (usingWorker, url) { |
||||
console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker')); |
||||
self.event.trigger('loadingCompiler', [url, usingWorker]); |
||||
console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker')) |
||||
self.event.trigger('loadingCompiler', [url, usingWorker]) |
||||
|
||||
if (usingWorker) { |
||||
loadWorker(url); |
||||
loadWorker(url) |
||||
} else { |
||||
loadInternal(url); |
||||
loadInternal(url) |
||||
} |
||||
}; |
||||
} |
||||
|
||||
function loadInternal (url) { |
||||
delete window.Module; |
||||
delete window.Module |
||||
// NOTE: workaround some browsers?
|
||||
window.Module = undefined; |
||||
window.Module = undefined |
||||
|
||||
// Set a safe fallback until the new one is loaded
|
||||
setCompileJSON(function (source, optimize) { |
||||
compilationFinished({error: 'Compiler not yet loaded.'}); |
||||
}); |
||||
compilationFinished({error: 'Compiler not yet loaded.'}) |
||||
}) |
||||
|
||||
var newScript = document.createElement('script'); |
||||
newScript.type = 'text/javascript'; |
||||
newScript.src = url; |
||||
document.getElementsByTagName('head')[0].appendChild(newScript); |
||||
var newScript = document.createElement('script') |
||||
newScript.type = 'text/javascript' |
||||
newScript.src = url |
||||
document.getElementsByTagName('head')[0].appendChild(newScript) |
||||
var check = window.setInterval(function () { |
||||
if (!window.Module) { |
||||
return; |
||||
return |
||||
} |
||||
window.clearInterval(check); |
||||
onInternalCompilerLoaded(); |
||||
}, 200); |
||||
window.clearInterval(check) |
||||
onInternalCompilerLoaded() |
||||
}, 200) |
||||
} |
||||
|
||||
function loadWorker (url) { |
||||
if (worker !== null) { |
||||
worker.terminate(); |
||||
worker.terminate() |
||||
} |
||||
worker = webworkify(require('./compiler-worker.js')); |
||||
var jobs = []; |
||||
worker = webworkify(require('./compiler-worker.js')) |
||||
var jobs = [] |
||||
worker.addEventListener('message', function (msg) { |
||||
var data = msg.data; |
||||
var data = msg.data |
||||
switch (data.cmd) { |
||||
case 'versionLoaded': |
||||
compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles; |
||||
onCompilerLoaded(data.data); |
||||
break; |
||||
compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles |
||||
onCompilerLoaded(data.data) |
||||
break |
||||
case 'compiled': |
||||
var result; |
||||
var result |
||||
try { |
||||
result = JSON.parse(data.data); |
||||
result = JSON.parse(data.data) |
||||
} catch (exception) { |
||||
result = { 'error': 'Invalid JSON output from the compiler: ' + exception }; |
||||
result = { 'error': 'Invalid JSON output from the compiler: ' + exception } |
||||
} |
||||
var sources = {}; |
||||
var sources = {} |
||||
if (data.job in jobs !== undefined) { |
||||
sources = jobs[data.job].sources; |
||||
delete jobs[data.job]; |
||||
sources = jobs[data.job].sources |
||||
delete jobs[data.job] |
||||
} |
||||
compilationFinished(result, data.missingInputs, sources); |
||||
break; |
||||
compilationFinished(result, data.missingInputs, sources) |
||||
break |
||||
} |
||||
}); |
||||
}) |
||||
worker.onerror = function (msg) { |
||||
compilationFinished({ error: 'Worker error: ' + msg.data }); |
||||
}; |
||||
compilationFinished({ error: 'Worker error: ' + msg.data }) |
||||
} |
||||
worker.addEventListener('error', function (msg) { |
||||
compilationFinished({ error: 'Worker error: ' + msg.data }); |
||||
}); |
||||
compilationFinished({ error: 'Worker error: ' + msg.data }) |
||||
}) |
||||
compileJSON = function (source, optimize) { |
||||
jobs.push({sources: source}); |
||||
worker.postMessage({cmd: 'compile', job: jobs.length - 1, source: JSON.stringify(source), optimize: optimize}); |
||||
}; |
||||
worker.postMessage({cmd: 'loadVersion', data: url}); |
||||
jobs.push({sources: source}) |
||||
worker.postMessage({cmd: 'compile', job: jobs.length - 1, source: JSON.stringify(source), optimize: optimize}) |
||||
} |
||||
worker.postMessage({cmd: 'loadVersion', data: url}) |
||||
} |
||||
|
||||
function gatherImports (files, importHints, cb) { |
||||
importHints = importHints || []; |
||||
importHints = importHints || [] |
||||
if (!compilerAcceptsMultipleFiles) { |
||||
cb(files[editor.getCacheFile()]); |
||||
return; |
||||
cb(files[editor.getCacheFile()]) |
||||
return |
||||
} |
||||
var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g; |
||||
var reloop = false; |
||||
var githubMatch; |
||||
var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g |
||||
var reloop = false |
||||
var githubMatch |
||||
do { |
||||
reloop = false; |
||||
reloop = false |
||||
for (var fileName in files) { |
||||
var match; |
||||
var match |
||||
while ((match = importRegex.exec(files[fileName]))) { |
||||
var importFilePath = match[1]; |
||||
var importFilePath = match[1] |
||||
if (importFilePath.startsWith('./')) { |
||||
importFilePath = importFilePath.slice(2); |
||||
importFilePath = importFilePath.slice(2) |
||||
} |
||||
importHints.push(importFilePath); |
||||
importHints.push(importFilePath) |
||||
} |
||||
} |
||||
while (importHints.length > 0) { |
||||
var m = importHints.pop(); |
||||
var m = importHints.pop() |
||||
if (m in files) { |
||||
continue; |
||||
continue |
||||
} |
||||
if (editor.hasFile(m)) { |
||||
files[m] = editor.getFile(m); |
||||
reloop = true; |
||||
files[m] = editor.getFile(m) |
||||
reloop = true |
||||
} else if (m in cachedRemoteFiles) { |
||||
files[m] = cachedRemoteFiles[m]; |
||||
reloop = true; |
||||
files[m] = cachedRemoteFiles[m] |
||||
reloop = true |
||||
} else if ((githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m))) { |
||||
handleGithubCall(githubMatch[3], githubMatch[4], function (err, content) { |
||||
if (err) { |
||||
cb(null, 'Unable to import "' + m + '": ' + err); |
||||
return; |
||||
cb(null, 'Unable to import "' + m + '": ' + err) |
||||
return |
||||
} |
||||
|
||||
cachedRemoteFiles[m] = content; |
||||
files[m] = content; |
||||
cachedRemoteFiles[m] = content |
||||
files[m] = content |
||||
|
||||
gatherImports(files, importHints, cb); |
||||
}); |
||||
return; |
||||
gatherImports(files, importHints, cb) |
||||
}) |
||||
return |
||||
} else if (/^[^:]*:\/\//.exec(m)) { |
||||
cb(null, 'Unable to import "' + m + '": Unsupported URL'); |
||||
return; |
||||
cb(null, 'Unable to import "' + m + '": Unsupported URL') |
||||
return |
||||
} else { |
||||
cb(null, 'Unable to import "' + m + '": File not found'); |
||||
return; |
||||
cb(null, 'Unable to import "' + m + '": File not found') |
||||
return |
||||
} |
||||
} |
||||
} while (reloop); |
||||
cb({ 'sources': files }); |
||||
} while (reloop) |
||||
cb({ 'sources': files }) |
||||
} |
||||
} |
||||
|
||||
module.exports = Compiler; |
||||
module.exports = Compiler |
||||
|
@ -1,194 +1,194 @@ |
||||
/* global FileReader */ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var utils = require('./utils'); |
||||
var examples = require('./example-contracts'); |
||||
var utils = require('./utils') |
||||
var examples = require('./example-contracts') |
||||
|
||||
var ace = require('brace'); |
||||
require('../mode-solidity.js'); |
||||
var ace = require('brace') |
||||
require('../mode-solidity.js') |
||||
|
||||
function Editor (loadingFromGist, storage) { |
||||
var SOL_CACHE_UNTITLED = utils.fileKey('Untitled'); |
||||
var SOL_CACHE_FILE = null; |
||||
var SOL_CACHE_UNTITLED = utils.fileKey('Untitled') |
||||
var SOL_CACHE_FILE = null |
||||
|
||||
var editor = ace.edit('input'); |
||||
var sessions = {}; |
||||
var sourceAnnotations = []; |
||||
var editor = ace.edit('input') |
||||
var sessions = {} |
||||
var sourceAnnotations = [] |
||||
|
||||
setupStuff(getFiles()); |
||||
setupStuff(getFiles()) |
||||
|
||||
this.addMarker = function (range, cssClass) { |
||||
return editor.session.addMarker(range, cssClass); |
||||
}; |
||||
return editor.session.addMarker(range, cssClass) |
||||
} |
||||
|
||||
this.removeMarker = function (markerId) { |
||||
editor.session.removeMarker(markerId); |
||||
}; |
||||
editor.session.removeMarker(markerId) |
||||
} |
||||
|
||||
this.newFile = function () { |
||||
var untitledCount = ''; |
||||
var untitledCount = '' |
||||
while (storage.exists(SOL_CACHE_UNTITLED + untitledCount)) { |
||||
untitledCount = (untitledCount - 0) + 1; |
||||
untitledCount = (untitledCount - 0) + 1 |
||||
} |
||||
SOL_CACHE_FILE = SOL_CACHE_UNTITLED + untitledCount; |
||||
this.setCacheFileContent(''); |
||||
}; |
||||
SOL_CACHE_FILE = SOL_CACHE_UNTITLED + untitledCount |
||||
this.setCacheFileContent('') |
||||
} |
||||
|
||||
this.uploadFile = function (file, callback) { |
||||
var fileReader = new FileReader(); |
||||
var cacheName = utils.fileKey(file.name); |
||||
var fileReader = new FileReader() |
||||
var cacheName = utils.fileKey(file.name) |
||||
|
||||
fileReader.onload = function (e) { |
||||
storage.set(cacheName, e.target.result); |
||||
SOL_CACHE_FILE = cacheName; |
||||
callback(); |
||||
}; |
||||
fileReader.readAsText(file); |
||||
}; |
||||
storage.set(cacheName, e.target.result) |
||||
SOL_CACHE_FILE = cacheName |
||||
callback() |
||||
} |
||||
fileReader.readAsText(file) |
||||
} |
||||
|
||||
this.setCacheFileContent = function (content) { |
||||
storage.set(SOL_CACHE_FILE, content); |
||||
}; |
||||
storage.set(SOL_CACHE_FILE, content) |
||||
} |
||||
|
||||
this.setCacheFile = function (cacheFile) { |
||||
SOL_CACHE_FILE = cacheFile; |
||||
}; |
||||
SOL_CACHE_FILE = cacheFile |
||||
} |
||||
|
||||
this.getCacheFile = function () { |
||||
return SOL_CACHE_FILE; |
||||
}; |
||||
return SOL_CACHE_FILE |
||||
} |
||||
|
||||
this.cacheFileIsPresent = function () { |
||||
return !!SOL_CACHE_FILE; |
||||
}; |
||||
return !!SOL_CACHE_FILE |
||||
} |
||||
|
||||
this.setNextFile = function (fileKey) { |
||||
var index = this.getFiles().indexOf(fileKey); |
||||
this.setCacheFile(this.getFiles()[ Math.max(0, index - 1) ]); |
||||
}; |
||||
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(); |
||||
}; |
||||
editor.setSession(sessions[SOL_CACHE_FILE]) |
||||
editor.focus() |
||||
} |
||||
|
||||
this.removeSession = function (fileKey) { |
||||
delete sessions[fileKey]; |
||||
}; |
||||
delete sessions[fileKey] |
||||
} |
||||
|
||||
this.renameSession = function (oldFileKey, newFileKey) { |
||||
if (oldFileKey !== newFileKey) { |
||||
sessions[newFileKey] = sessions[oldFileKey]; |
||||
this.removeSession(oldFileKey); |
||||
sessions[newFileKey] = sessions[oldFileKey] |
||||
this.removeSession(oldFileKey) |
||||
} |
||||
}; |
||||
} |
||||
|
||||
this.hasFile = function (name) { |
||||
return this.getFiles().indexOf(utils.fileKey(name)) !== -1; |
||||
}; |
||||
return this.getFiles().indexOf(utils.fileKey(name)) !== -1 |
||||
} |
||||
|
||||
this.getFile = function (name) { |
||||
return storage.get(utils.fileKey(name)); |
||||
}; |
||||
return storage.get(utils.fileKey(name)) |
||||
} |
||||
|
||||
function getFiles () { |
||||
var files = []; |
||||
var files = [] |
||||
storage.keys().forEach(function (f) { |
||||
if (utils.isCachedFile(f)) { |
||||
files.push(f); |
||||
if (!sessions[f]) sessions[f] = newEditorSession(f); |
||||
files.push(f) |
||||
if (!sessions[f]) sessions[f] = newEditorSession(f) |
||||
} |
||||
}); |
||||
return files; |
||||
}) |
||||
return files |
||||
} |
||||
this.getFiles = getFiles; |
||||
this.getFiles = getFiles |
||||
|
||||
this.packageFiles = function () { |
||||
var files = {}; |
||||
var filesArr = this.getFiles(); |
||||
var files = {} |
||||
var filesArr = this.getFiles() |
||||
|
||||
for (var f in filesArr) { |
||||
files[utils.fileNameFromKey(filesArr[f])] = { |
||||
content: storage.get(filesArr[f]) |
||||
}; |
||||
} |
||||
} |
||||
return files; |
||||
}; |
||||
return files |
||||
} |
||||
|
||||
this.resize = function () { |
||||
editor.resize(); |
||||
var session = editor.getSession(); |
||||
session.setUseWrapMode(document.querySelector('#editorWrap').checked); |
||||
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; |
||||
var characterWidth = editor.renderer.characterWidth |
||||
var contentWidth = editor.container.ownerDocument.getElementsByClassName('ace_scroller')[0].clientWidth |
||||
|
||||
if (contentWidth > 0) { |
||||
session.setWrapLimit(parseInt(contentWidth / characterWidth, 10)); |
||||
session.setWrapLimit(parseInt(contentWidth / characterWidth, 10)) |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
|
||||
this.getValue = function () { |
||||
return editor.getValue(); |
||||
}; |
||||
return editor.getValue() |
||||
} |
||||
|
||||
this.clearAnnotations = function () { |
||||
sourceAnnotations = []; |
||||
editor.getSession().clearAnnotations(); |
||||
}; |
||||
sourceAnnotations = [] |
||||
editor.getSession().clearAnnotations() |
||||
} |
||||
|
||||
this.addAnnotation = function (annotation) { |
||||
sourceAnnotations[sourceAnnotations.length] = annotation; |
||||
this.setAnnotations(sourceAnnotations); |
||||
}; |
||||
sourceAnnotations[sourceAnnotations.length] = annotation |
||||
this.setAnnotations(sourceAnnotations) |
||||
} |
||||
|
||||
this.setAnnotations = function (sourceAnnotations) { |
||||
editor.getSession().setAnnotations(sourceAnnotations); |
||||
}; |
||||
editor.getSession().setAnnotations(sourceAnnotations) |
||||
} |
||||
|
||||
this.onChangeSetup = function (onChange) { |
||||
editor.getSession().on('change', onChange); |
||||
editor.getSession().on('change', onChange) |
||||
editor.on('changeSession', function () { |
||||
editor.getSession().on('change', onChange); |
||||
onChange(); |
||||
}); |
||||
}; |
||||
editor.getSession().on('change', onChange) |
||||
onChange() |
||||
}) |
||||
} |
||||
|
||||
this.handleErrorClick = function (errLine, errCol) { |
||||
editor.focus(); |
||||
editor.gotoLine(errLine + 1, errCol - 1, true); |
||||
}; |
||||
editor.focus() |
||||
editor.gotoLine(errLine + 1, errCol - 1, true) |
||||
} |
||||
|
||||
function newEditorSession (filekey) { |
||||
var s = new ace.EditSession(storage.get(filekey), 'ace/mode/javascript'); |
||||
s.setUndoManager(new ace.UndoManager()); |
||||
s.setTabSize(4); |
||||
s.setUseSoftTabs(true); |
||||
sessions[filekey] = s; |
||||
return s; |
||||
var s = new ace.EditSession(storage.get(filekey), 'ace/mode/javascript') |
||||
s.setUndoManager(new ace.UndoManager()) |
||||
s.setTabSize(4) |
||||
s.setUseSoftTabs(true) |
||||
sessions[filekey] = s |
||||
return s |
||||
} |
||||
|
||||
function setupStuff (files) { |
||||
if (files.length === 0) { |
||||
if (loadingFromGist) return; |
||||
files.push(utils.fileKey(examples.ballot.name)); |
||||
storage.set(utils.fileKey(examples.ballot.name), examples.ballot.content); |
||||
if (loadingFromGist) return |
||||
files.push(utils.fileKey(examples.ballot.name)) |
||||
storage.set(utils.fileKey(examples.ballot.name), examples.ballot.content) |
||||
} |
||||
|
||||
SOL_CACHE_FILE = files[0]; |
||||
SOL_CACHE_FILE = files[0] |
||||
|
||||
for (var x in files) { |
||||
sessions[files[x]] = newEditorSession(files[x]); |
||||
sessions[files[x]] = newEditorSession(files[x]) |
||||
} |
||||
|
||||
editor.setSession(sessions[SOL_CACHE_FILE]); |
||||
editor.resize(true); |
||||
editor.setSession(sessions[SOL_CACHE_FILE]) |
||||
editor.resize(true) |
||||
|
||||
// Unmap ctrl-t & ctrl-f
|
||||
editor.commands.bindKeys({ 'ctrl-t': null }); |
||||
editor.commands.bindKeys({ 'ctrl-f': null }); |
||||
editor.commands.bindKeys({ 'ctrl-t': null }) |
||||
editor.commands.bindKeys({ 'ctrl-f': null }) |
||||
} |
||||
} |
||||
|
||||
module.exports = Editor; |
||||
module.exports = Editor |
||||
|
@ -1,69 +1,69 @@ |
||||
var ballot = `pragma solidity ^0.4.0;
|
||||
var ballot = `pragma solidity ^0.4.0
|
||||
contract Ballot { |
||||
|
||||
struct Voter { |
||||
uint weight; |
||||
bool voted; |
||||
uint8 vote; |
||||
address delegate; |
||||
uint weight |
||||
bool voted |
||||
uint8 vote |
||||
address delegate |
||||
} |
||||
struct Proposal { |
||||
uint voteCount; |
||||
uint voteCount |
||||
} |
||||
|
||||
address chairperson; |
||||
mapping(address => Voter) voters; |
||||
Proposal[] proposals; |
||||
address chairperson |
||||
mapping(address => Voter) voters |
||||
Proposal[] proposals |
||||
|
||||
/// Create a new ballot with $(_numProposals) different proposals.
|
||||
function Ballot(uint8 _numProposals) { |
||||
chairperson = msg.sender; |
||||
voters[chairperson].weight = 1; |
||||
proposals.length = _numProposals; |
||||
chairperson = msg.sender |
||||
voters[chairperson].weight = 1 |
||||
proposals.length = _numProposals |
||||
} |
||||
|
||||
/// Give $(voter) the right to vote on this ballot.
|
||||
/// May only be called by $(chairperson).
|
||||
function giveRightToVote(address voter) { |
||||
if (msg.sender != chairperson || voters[voter].voted) return; |
||||
voters[voter].weight = 1; |
||||
if (msg.sender != chairperson || voters[voter].voted) return |
||||
voters[voter].weight = 1 |
||||
} |
||||
|
||||
/// Delegate your vote to the voter $(to).
|
||||
function delegate(address to) { |
||||
Voter sender = voters[msg.sender]; // assigns reference
|
||||
if (sender.voted) return; |
||||
Voter sender = voters[msg.sender] // assigns reference
|
||||
if (sender.voted) return |
||||
while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender) |
||||
to = voters[to].delegate; |
||||
if (to == msg.sender) return; |
||||
sender.voted = true; |
||||
sender.delegate = to; |
||||
Voter delegate = voters[to]; |
||||
to = voters[to].delegate |
||||
if (to == msg.sender) return |
||||
sender.voted = true |
||||
sender.delegate = to |
||||
Voter delegate = voters[to] |
||||
if (delegate.voted) |
||||
proposals[delegate.vote].voteCount += sender.weight; |
||||
proposals[delegate.vote].voteCount += sender.weight |
||||
else |
||||
delegate.weight += sender.weight; |
||||
delegate.weight += sender.weight |
||||
} |
||||
|
||||
/// Give a single vote to proposal $(proposal).
|
||||
function vote(uint8 proposal) { |
||||
Voter sender = voters[msg.sender]; |
||||
if (sender.voted || proposal >= proposals.length) return; |
||||
sender.voted = true; |
||||
sender.vote = proposal; |
||||
proposals[proposal].voteCount += sender.weight; |
||||
Voter sender = voters[msg.sender] |
||||
if (sender.voted || proposal >= proposals.length) return |
||||
sender.voted = true |
||||
sender.vote = proposal |
||||
proposals[proposal].voteCount += sender.weight |
||||
} |
||||
|
||||
function winningProposal() constant returns (uint8 winningProposal) { |
||||
uint256 winningVoteCount = 0; |
||||
uint256 winningVoteCount = 0 |
||||
for (uint8 proposal = 0; proposal < proposals.length; proposal++) |
||||
if (proposals[proposal].voteCount > winningVoteCount) { |
||||
winningVoteCount = proposals[proposal].voteCount; |
||||
winningProposal = proposal; |
||||
winningVoteCount = proposals[proposal].voteCount |
||||
winningProposal = proposal |
||||
} |
||||
} |
||||
}`;
|
||||
}` |
||||
|
||||
module.exports = { |
||||
ballot: { name: 'ballot.sol', content: ballot } |
||||
}; |
||||
} |
||||
|
@ -1,121 +1,121 @@ |
||||
/* global confirm */ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var $ = require('jquery'); |
||||
var Web3 = require('web3'); |
||||
var EventManager = require('../lib/eventManager'); |
||||
var EthJSVM = require('ethereumjs-vm'); |
||||
var $ = require('jquery') |
||||
var Web3 = require('web3') |
||||
var EventManager = require('../lib/eventManager') |
||||
var EthJSVM = require('ethereumjs-vm') |
||||
|
||||
var injectedProvider; |
||||
var injectedProvider |
||||
|
||||
var web3; |
||||
var web3 |
||||
if (typeof window.web3 !== 'undefined') { |
||||
injectedProvider = window.web3.currentProvider; |
||||
web3 = new Web3(injectedProvider); |
||||
injectedProvider = window.web3.currentProvider |
||||
web3 = new Web3(injectedProvider) |
||||
} else { |
||||
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); |
||||
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) |
||||
} |
||||
|
||||
var vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true }); |
||||
vm.stateManager.checkpoint(); |
||||
var vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true }) |
||||
vm.stateManager.checkpoint() |
||||
|
||||
/* |
||||
trigger contextChanged, web3EndpointChanged |
||||
*/ |
||||
function ExecutionContext () { |
||||
var self = this; |
||||
this.event = new EventManager(); |
||||
var executionContext = injectedProvider ? 'injected' : 'vm'; |
||||
var self = this |
||||
this.event = new EventManager() |
||||
var executionContext = injectedProvider ? 'injected' : 'vm' |
||||
|
||||
this.isVM = function () { |
||||
return executionContext === 'vm'; |
||||
}; |
||||
return executionContext === 'vm' |
||||
} |
||||
|
||||
this.web3 = function () { |
||||
return web3; |
||||
}; |
||||
return web3 |
||||
} |
||||
|
||||
this.vm = function () { |
||||
return vm; |
||||
}; |
||||
return vm |
||||
} |
||||
|
||||
this.setEndPointUrl = function (url) { |
||||
$web3endpoint.val(url); |
||||
}; |
||||
$web3endpoint.val(url) |
||||
} |
||||
|
||||
this.setContext = function (context) { |
||||
executionContext = context; |
||||
executionContextChange(context); |
||||
setExecutionContextRadio(); |
||||
}; |
||||
executionContext = context |
||||
executionContextChange(context) |
||||
setExecutionContextRadio() |
||||
} |
||||
|
||||
var $injectedToggle = $('#injected-mode'); |
||||
var $vmToggle = $('#vm-mode'); |
||||
var $web3Toggle = $('#web3-mode'); |
||||
var $web3endpoint = $('#web3Endpoint'); |
||||
var $injectedToggle = $('#injected-mode') |
||||
var $vmToggle = $('#vm-mode') |
||||
var $web3Toggle = $('#web3-mode') |
||||
var $web3endpoint = $('#web3Endpoint') |
||||
|
||||
if (web3.providers && web3.currentProvider instanceof web3.providers.IpcProvider) { |
||||
$web3endpoint.val('ipc'); |
||||
$web3endpoint.val('ipc') |
||||
} |
||||
|
||||
setExecutionContextRadio(); |
||||
setExecutionContextRadio() |
||||
|
||||
$injectedToggle.on('change', executionContextUIChange); |
||||
$vmToggle.on('change', executionContextUIChange); |
||||
$web3Toggle.on('change', executionContextUIChange); |
||||
$injectedToggle.on('change', executionContextUIChange) |
||||
$vmToggle.on('change', executionContextUIChange) |
||||
$web3Toggle.on('change', executionContextUIChange) |
||||
$web3endpoint.on('change', function () { |
||||
setProviderFromEndpoint(); |
||||
setProviderFromEndpoint() |
||||
if (executionContext === 'web3') { |
||||
self.event.trigger('web3EndpointChanged'); |
||||
self.event.trigger('web3EndpointChanged') |
||||
} |
||||
}); |
||||
}) |
||||
|
||||
function executionContextUIChange (ev) { |
||||
executionContextChange(ev.target.value); |
||||
executionContextChange(ev.target.value) |
||||
} |
||||
|
||||
function executionContextChange (context) { |
||||
if (context === 'web3' && !confirm('Are you sure you want to connect to a local ethereum node?')) { |
||||
setExecutionContextRadio(); |
||||
setExecutionContextRadio() |
||||
} else if (context === 'injected' && injectedProvider === undefined) { |
||||
setExecutionContextRadio(); |
||||
setExecutionContextRadio() |
||||
} else { |
||||
if (context === 'web3') { |
||||
executionContext = context; |
||||
setProviderFromEndpoint(); |
||||
self.event.trigger('contextChanged', ['web3']); |
||||
executionContext = context |
||||
setProviderFromEndpoint() |
||||
self.event.trigger('contextChanged', ['web3']) |
||||
} else if (context === 'injected') { |
||||
executionContext = context; |
||||
web3.setProvider(injectedProvider); |
||||
self.event.trigger('contextChanged', ['injected']); |
||||
executionContext = context |
||||
web3.setProvider(injectedProvider) |
||||
self.event.trigger('contextChanged', ['injected']) |
||||
} else if (context === 'vm') { |
||||
executionContext = context; |
||||
executionContext = context |
||||
vm.stateManager.revert(function () { |
||||
vm.stateManager.checkpoint(); |
||||
}); |
||||
self.event.trigger('contextChanged', ['vm']); |
||||
vm.stateManager.checkpoint() |
||||
}) |
||||
self.event.trigger('contextChanged', ['vm']) |
||||
} |
||||
} |
||||
} |
||||
|
||||
function setProviderFromEndpoint () { |
||||
var endpoint = $web3endpoint.val(); |
||||
var endpoint = $web3endpoint.val() |
||||
if (endpoint === 'ipc') { |
||||
web3.setProvider(new web3.providers.IpcProvider()); |
||||
web3.setProvider(new web3.providers.IpcProvider()) |
||||
} else { |
||||
web3.setProvider(new web3.providers.HttpProvider(endpoint)); |
||||
web3.setProvider(new web3.providers.HttpProvider(endpoint)) |
||||
} |
||||
} |
||||
|
||||
function setExecutionContextRadio () { |
||||
if (executionContext === 'injected') { |
||||
$injectedToggle.get(0).checked = true; |
||||
$injectedToggle.get(0).checked = true |
||||
} else if (executionContext === 'vm') { |
||||
$vmToggle.get(0).checked = true; |
||||
$vmToggle.get(0).checked = true |
||||
} else if (executionContext === 'web3') { |
||||
$web3Toggle.get(0).checked = true; |
||||
$web3Toggle.get(0).checked = true |
||||
} |
||||
} |
||||
} |
||||
|
||||
module.exports = ExecutionContext; |
||||
module.exports = ExecutionContext |
||||
|
@ -1,48 +1,48 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var $ = require('jquery'); |
||||
var EventManager = require('../lib/eventManager'); |
||||
var $ = require('jquery') |
||||
var EventManager = require('../lib/eventManager') |
||||
|
||||
/* |
||||
trigger compilationFinished |
||||
*/ |
||||
function FormalVerification (outputElement, compilerEvent) { |
||||
this.event = new EventManager(); |
||||
this.outputElement = outputElement; |
||||
var self = this; |
||||
this.event = new EventManager() |
||||
this.outputElement = outputElement |
||||
var self = this |
||||
compilerEvent.register('compilationFinished', this, function (success, data, source) { |
||||
if (success) { |
||||
self.compilationFinished(data); |
||||
self.compilationFinished(data) |
||||
} |
||||
}); |
||||
}) |
||||
compilerEvent.register('compilationStarted', this, function () { |
||||
$('#formalVerificationInput', self.outputElement) |
||||
.val('') |
||||
.hide(); |
||||
$('#formalVerificationErrors').empty(); |
||||
}); |
||||
.val('') |
||||
.hide() |
||||
$('#formalVerificationErrors').empty() |
||||
}) |
||||
} |
||||
|
||||
FormalVerification.prototype.compilationFinished = function (compilationResult) { |
||||
if (compilationResult.formal === undefined) { |
||||
this.event.trigger('compilationFinished', [false, 'Formal verification not supported by this compiler version.', $('#formalVerificationErrors'), true]); |
||||
this.event.trigger('compilationFinished', [false, 'Formal verification not supported by this compiler version.', $('#formalVerificationErrors'), true]) |
||||
} else { |
||||
if (compilationResult.formal['why3'] !== undefined) { |
||||
$('#formalVerificationInput', this.outputElement).val( |
||||
'(* copy this to http://why3.lri.fr/try/ *)' + |
||||
compilationResult.formal['why3'] |
||||
) |
||||
.show(); |
||||
.show() |
||||
} |
||||
if (compilationResult.formal.errors !== undefined) { |
||||
var errors = compilationResult.formal.errors; |
||||
var errors = compilationResult.formal.errors |
||||
for (var i = 0; i < errors.length; i++) { |
||||
this.event.trigger('compilationFinished', [false, errors[i], $('#formalVerificationErrors'), true]); |
||||
this.event.trigger('compilationFinished', [false, errors[i], $('#formalVerificationErrors'), true]) |
||||
} |
||||
} else { |
||||
this.event.trigger('compilationFinished', [true, null, null, true]); |
||||
this.event.trigger('compilationFinished', [true, null, null, true]) |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
|
||||
module.exports = FormalVerification; |
||||
module.exports = FormalVerification |
||||
|
@ -1,33 +1,33 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
// Allowing window to be overriden for testing
|
||||
function GistHandler (_window) { |
||||
if (_window === undefined) _window = window; |
||||
if (_window === undefined) _window = window |
||||
|
||||
this.handleLoad = function (params, cb) { |
||||
var loadingFromGist = false; |
||||
var gistId; |
||||
var loadingFromGist = false |
||||
var gistId |
||||
if (params['gist'] === '') { |
||||
var str = _window.prompt('Enter the URL or ID of the Gist you would like to load.'); |
||||
var str = _window.prompt('Enter the URL or ID of the Gist you would like to load.') |
||||
if (str !== '') { |
||||
gistId = getGistId(str); |
||||
loadingFromGist = !!gistId; |
||||
gistId = getGistId(str) |
||||
loadingFromGist = !!gistId |
||||
} |
||||
} else { |
||||
gistId = params['gist']; |
||||
loadingFromGist = !!gistId; |
||||
gistId = params['gist'] |
||||
loadingFromGist = !!gistId |
||||
} |
||||
if (loadingFromGist) { |
||||
cb(gistId); |
||||
cb(gistId) |
||||
} |
||||
return loadingFromGist; |
||||
}; |
||||
return loadingFromGist |
||||
} |
||||
|
||||
function getGistId (str) { |
||||
var idr = /[0-9A-Fa-f]{8,}/; |
||||
var match = idr.exec(str); |
||||
return match ? match[0] : null; |
||||
var idr = /[0-9A-Fa-f]{8,}/ |
||||
var match = idr.exec(str) |
||||
return match ? match[0] : null |
||||
} |
||||
} |
||||
|
||||
module.exports = GistHandler; |
||||
module.exports = GistHandler |
||||
|
@ -1,42 +1,42 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
// Allowing window to be overriden for testing
|
||||
function QueryParams (_window) { |
||||
if (_window === undefined) _window = window; |
||||
if (_window === undefined) _window = window |
||||
|
||||
this.get = function () { |
||||
var qs = _window.location.hash.substr(1); |
||||
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 = ''; |
||||
_window.location.hash = _window.location.search.substr(1) |
||||
_window.location.search = '' |
||||
} |
||||
|
||||
var params = {}; |
||||
var parts = qs.split('&'); |
||||
var params = {} |
||||
var parts = qs.split('&') |
||||
for (var x in parts) { |
||||
var keyValue = parts[x].split('='); |
||||
var keyValue = parts[x].split('=') |
||||
if (keyValue[0] !== '') { |
||||
params[keyValue[0]] = keyValue[1]; |
||||
params[keyValue[0]] = keyValue[1] |
||||
} |
||||
} |
||||
return params; |
||||
}; |
||||
return params |
||||
} |
||||
|
||||
this.update = function (params) { |
||||
var currentParams = this.get(); |
||||
var keys = Object.keys(params); |
||||
var currentParams = this.get() |
||||
var keys = Object.keys(params) |
||||
for (var x in keys) { |
||||
currentParams[keys[x]] = params[keys[x]]; |
||||
currentParams[keys[x]] = params[keys[x]] |
||||
} |
||||
var queryString = '#'; |
||||
var updatedKeys = Object.keys(currentParams); |
||||
var queryString = '#' |
||||
var updatedKeys = Object.keys(currentParams) |
||||
for (var y in updatedKeys) { |
||||
queryString += updatedKeys[y] + '=' + currentParams[updatedKeys[y]] + '&'; |
||||
queryString += updatedKeys[y] + '=' + currentParams[updatedKeys[y]] + '&' |
||||
} |
||||
_window.location.hash = queryString.slice(0, -1); |
||||
}; |
||||
_window.location.hash = queryString.slice(0, -1) |
||||
} |
||||
} |
||||
|
||||
module.exports = QueryParams; |
||||
module.exports = QueryParams |
||||
|
@ -1,149 +1,149 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var $ = require('jquery'); |
||||
var $ = require('jquery') |
||||
|
||||
var utils = require('./utils'); |
||||
var uiHelper = require('./ui-helper'); |
||||
var utils = require('./utils') |
||||
var uiHelper = require('./ui-helper') |
||||
|
||||
function Renderer (editor, web3, updateFiles, udapp, executionContext, formalVerificationEvent, compilerEvent) { |
||||
this.editor = editor; |
||||
this.web3 = web3; |
||||
this.updateFiles = updateFiles; |
||||
this.udapp = udapp; |
||||
this.executionContext = executionContext; |
||||
var self = this; |
||||
this.editor = editor |
||||
this.web3 = web3 |
||||
this.updateFiles = updateFiles |
||||
this.udapp = udapp |
||||
this.executionContext = executionContext |
||||
var self = this |
||||
formalVerificationEvent.register('compilationFinished', this, function (success, message, container, noAnnotations) { |
||||
if (!success) { |
||||
self.error(message, container, noAnnotations); |
||||
self.error(message, container, noAnnotations) |
||||
} |
||||
}); |
||||
}) |
||||
compilerEvent.register('compilationFinished', this, function (success, data, source) { |
||||
$('#output').empty(); |
||||
$('#output').empty() |
||||
if (success) { |
||||
self.contracts(data, source); |
||||
self.contracts(data, source) |
||||
} |
||||
|
||||
// NOTE: still need to display as there might be warnings
|
||||
if (data['error']) { |
||||
self.error(data['error']); |
||||
self.error(data['error']) |
||||
} |
||||
if (data['errors']) { |
||||
data['errors'].forEach(function (err) { |
||||
self.error(err); |
||||
}); |
||||
self.error(err) |
||||
}) |
||||
} |
||||
}); |
||||
}) |
||||
} |
||||
|
||||
Renderer.prototype.error = function (message, container, noAnnotations) { |
||||
var self = this; |
||||
var type = utils.errortype(message); |
||||
var $pre = $('<pre />').text(message); |
||||
var $error = $('<div class="sol ' + type + '"><div class="close"><i class="fa fa-close"></i></div></div>').prepend($pre); |
||||
var self = this |
||||
var type = utils.errortype(message) |
||||
var $pre = $('<pre />').text(message) |
||||
var $error = $('<div class="sol ' + type + '"><div class="close"><i class="fa fa-close"></i></div></div>').prepend($pre) |
||||
if (container === undefined) { |
||||
container = $('#output'); |
||||
container = $('#output') |
||||
} |
||||
container.append($error); |
||||
var err = message.match(/^([^:]*):([0-9]*):(([0-9]*):)? /); |
||||
container.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; |
||||
var errFile = err[1] |
||||
var errLine = parseInt(err[2], 10) - 1 |
||||
var errCol = err[4] ? parseInt(err[4], 10) : 0 |
||||
if (!noAnnotations && (errFile === '' || errFile === utils.fileNameFromKey(self.editor.getCacheFile()))) { |
||||
self.editor.addAnnotation({ |
||||
row: errLine, |
||||
column: errCol, |
||||
text: message, |
||||
type: type |
||||
}); |
||||
}) |
||||
} |
||||
$error.click(function (ev) { |
||||
if (errFile !== '' && errFile !== utils.fileNameFromKey(self.editor.getCacheFile()) && self.editor.hasFile(errFile)) { |
||||
// Switch to file
|
||||
self.editor.setCacheFile(utils.fileKey(errFile)); |
||||
self.updateFiles(); |
||||
self.editor.setCacheFile(utils.fileKey(errFile)) |
||||
self.updateFiles() |
||||
// @TODO could show some error icon in files with errors
|
||||
} |
||||
self.editor.handleErrorClick(errLine, errCol); |
||||
}); |
||||
self.editor.handleErrorClick(errLine, errCol) |
||||
}) |
||||
$error.find('.close').click(function (ev) { |
||||
ev.preventDefault(); |
||||
$error.remove(); |
||||
return false; |
||||
}); |
||||
ev.preventDefault() |
||||
$error.remove() |
||||
return false |
||||
}) |
||||
} |
||||
}; |
||||
} |
||||
|
||||
Renderer.prototype.contracts = function (data, source) { |
||||
var udappContracts = []; |
||||
var udappContracts = [] |
||||
for (var contractName in data.contracts) { |
||||
var contract = data.contracts[contractName]; |
||||
var contract = data.contracts[contractName] |
||||
udappContracts.push({ |
||||
name: contractName, |
||||
interface: contract['interface'], |
||||
bytecode: contract.bytecode |
||||
}); |
||||
}) |
||||
} |
||||
|
||||
// rendering function used by udapp. they need data and source
|
||||
var combined = function (contractName, jsonInterface, bytecode) { |
||||
return JSON.stringify([{ name: contractName, interface: jsonInterface, bytecode: bytecode }]); |
||||
}; |
||||
return JSON.stringify([{ name: contractName, interface: jsonInterface, bytecode: bytecode }]) |
||||
} |
||||
|
||||
var renderOutputModifier = function (contractName, $contractOutput) { |
||||
var contract = data.contracts[contractName]; |
||||
var contract = data.contracts[contractName] |
||||
if (contract.bytecode) { |
||||
$contractOutput.append(uiHelper.textRow('Bytecode', contract.bytecode)); |
||||
$contractOutput.append(uiHelper.textRow('Bytecode', contract.bytecode)) |
||||
} |
||||
|
||||
$contractOutput.append(uiHelper.textRow('Interface', contract['interface'])); |
||||
$contractOutput.append(uiHelper.textRow('Interface', contract['interface'])) |
||||
|
||||
if (contract.bytecode) { |
||||
$contractOutput.append(uiHelper.textRow('Web3 deploy', uiHelper.gethDeploy(contractName.toLowerCase(), contract['interface'], contract.bytecode), 'deploy')); |
||||
$contractOutput.append(uiHelper.textRow('uDApp', combined(contractName, contract['interface'], contract.bytecode), 'deploy')); |
||||
$contractOutput.append(uiHelper.textRow('Web3 deploy', uiHelper.gethDeploy(contractName.toLowerCase(), contract['interface'], contract.bytecode), 'deploy')) |
||||
$contractOutput.append(uiHelper.textRow('uDApp', combined(contractName, contract['interface'], contract.bytecode), 'deploy')) |
||||
} |
||||
var ctrSource = getSource(contractName, source, data); |
||||
return $contractOutput.append(uiHelper.getDetails(contract, ctrSource, contractName)); |
||||
}; |
||||
var ctrSource = getSource(contractName, source, data) |
||||
return $contractOutput.append(uiHelper.getDetails(contract, ctrSource, contractName)) |
||||
} |
||||
// //
|
||||
var self = this; |
||||
var self = this |
||||
|
||||
var getSource = function (contractName, source, data) { |
||||
var currentFile = utils.fileNameFromKey(self.editor.getCacheFile()); |
||||
return source.sources[currentFile]; |
||||
}; |
||||
var currentFile = utils.fileNameFromKey(self.editor.getCacheFile()) |
||||
return source.sources[currentFile] |
||||
} |
||||
|
||||
var getAddress = function () { return $('#txorigin').val(); }; |
||||
var getAddress = function () { return $('#txorigin').val() } |
||||
|
||||
var getValue = function () { |
||||
var comp = $('#value').val().split(' '); |
||||
return self.executionContext.web3().toWei(comp[0], comp.slice(1).join(' ')); |
||||
}; |
||||
var comp = $('#value').val().split(' ') |
||||
return self.executionContext.web3().toWei(comp[0], comp.slice(1).join(' ')) |
||||
} |
||||
|
||||
var getGasLimit = function () { return $('#gasLimit').val(); }; |
||||
var getGasLimit = function () { return $('#gasLimit').val() } |
||||
|
||||
this.udapp.reset(udappContracts, getAddress, getValue, getGasLimit, renderOutputModifier); |
||||
this.udapp.reset(udappContracts, getAddress, getValue, getGasLimit, renderOutputModifier) |
||||
|
||||
var $contractOutput = this.udapp.render(); |
||||
var $contractOutput = this.udapp.render() |
||||
|
||||
var $txOrigin = $('#txorigin'); |
||||
var $txOrigin = $('#txorigin') |
||||
|
||||
this.udapp.getAccounts(function (err, accounts) { |
||||
if (err) { |
||||
self.error(err.message); |
||||
self.error(err.message) |
||||
} |
||||
if (accounts && accounts[0]) { |
||||
$txOrigin.empty(); |
||||
for (var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])); } |
||||
$txOrigin.val(accounts[0]); |
||||
$txOrigin.empty() |
||||
for (var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])) } |
||||
$txOrigin.val(accounts[0]) |
||||
} else { |
||||
$txOrigin.val('unknown'); |
||||
$txOrigin.val('unknown') |
||||
} |
||||
}); |
||||
}) |
||||
|
||||
$contractOutput.find('.title').click(function (ev) { $(this).closest('.contract').toggleClass('hide'); }); |
||||
$('#output').append($contractOutput); |
||||
$('.col2 input,textarea').click(function () { this.select(); }); |
||||
}; |
||||
$contractOutput.find('.title').click(function (ev) { $(this).closest('.contract').toggleClass('hide') }) |
||||
$('#output').append($contractOutput) |
||||
$('.col2 input,textarea').click(function () { this.select() }) |
||||
} |
||||
|
||||
module.exports = Renderer; |
||||
module.exports = Renderer |
||||
|
@ -1,98 +1,98 @@ |
||||
/* global chrome, confirm */ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var utils = require('./utils'); |
||||
var utils = require('./utils') |
||||
|
||||
function Storage (updateFiles) { |
||||
var EDITOR_SIZE_CACHE_KEY = 'editor-size-cache'; |
||||
var EDITOR_SIZE_CACHE_KEY = 'editor-size-cache' |
||||
|
||||
this.rename = function (originalName, newName) { |
||||
var content = this.get(originalName); |
||||
this.set(newName, content); |
||||
this.remove(originalName); |
||||
}; |
||||
var content = this.get(originalName) |
||||
this.set(newName, content) |
||||
this.remove(originalName) |
||||
} |
||||
|
||||
this.remove = function (name) { |
||||
window.localStorage.removeItem(name); |
||||
}; |
||||
window.localStorage.removeItem(name) |
||||
} |
||||
|
||||
this.setEditorSize = function (size) { |
||||
this.set(EDITOR_SIZE_CACHE_KEY, size); |
||||
}; |
||||
this.set(EDITOR_SIZE_CACHE_KEY, size) |
||||
} |
||||
|
||||
this.getEditorSize = function () { |
||||
return this.get(EDITOR_SIZE_CACHE_KEY); |
||||
}; |
||||
return this.get(EDITOR_SIZE_CACHE_KEY) |
||||
} |
||||
|
||||
this.getFileContent = function (key) { |
||||
return this.get(utils.fileKey(key)); |
||||
}; |
||||
return this.get(utils.fileKey(key)) |
||||
} |
||||
|
||||
this.exists = function (key) { |
||||
return !!this.get(key); |
||||
}; |
||||
return !!this.get(key) |
||||
} |
||||
|
||||
this.set = function (key, content) { |
||||
window.localStorage.setItem(key, content); |
||||
}; |
||||
window.localStorage.setItem(key, content) |
||||
} |
||||
|
||||
this.get = function (key) { |
||||
return window.localStorage.getItem(key); |
||||
}; |
||||
return window.localStorage.getItem(key) |
||||
} |
||||
|
||||
this.keys = function () { |
||||
// NOTE: this is a workaround for some browsers
|
||||
return Object.keys(window.localStorage).filter(function (item) { return item !== null && item !== undefined; }); |
||||
}; |
||||
return Object.keys(window.localStorage).filter(function (item) { return item !== null && item !== undefined }) |
||||
} |
||||
|
||||
this.loadFile = function (filename, content) { |
||||
if (this.exists(filename) && this.get(filename) !== content) { |
||||
var count = ''; |
||||
while (this.exists(filename + count)) count = count - 1; |
||||
this.rename(filename, filename + count); |
||||
var count = '' |
||||
while (this.exists(filename + count)) count = count - 1 |
||||
this.rename(filename, filename + count) |
||||
} |
||||
this.set(filename, content); |
||||
}; |
||||
this.set(filename, content) |
||||
} |
||||
|
||||
this.sync = function () { |
||||
if (typeof chrome === 'undefined' || !chrome || !chrome.storage || !chrome.storage.sync) { |
||||
return; |
||||
return |
||||
} |
||||
|
||||
var obj = {}; |
||||
var done = false; |
||||
var count = 0; |
||||
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); |
||||
console.log('comparing to cloud', key, resp) |
||||
if (typeof resp[key] !== 'undefined' && obj[key] !== resp[key] && confirm('Overwrite "' + utils.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); |
||||
window.localStorage.setItem(key, resp[key]); |
||||
updateFiles(); |
||||
console.log('Overwriting', key) |
||||
window.localStorage.setItem(key, resp[key]) |
||||
updateFiles() |
||||
} else { |
||||
console.log('add to obj', obj, key); |
||||
obj[key] = window.localStorage[key]; |
||||
console.log('add to obj', obj, key) |
||||
obj[key] = window.localStorage[key] |
||||
} |
||||
done++; |
||||
done++ |
||||
if (done >= count) { |
||||
chrome.storage.sync.set(obj, function () { |
||||
console.log('updated cloud files with: ', obj, this, arguments); |
||||
}); |
||||
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); |
||||
console.log('checking', y) |
||||
obj[y] = window.localStorage.getItem(y) |
||||
if (!utils.isCachedFile(y)) { |
||||
continue; |
||||
continue |
||||
} |
||||
count++; |
||||
check(y); |
||||
count++ |
||||
check(y) |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
|
||||
module.exports = Storage; |
||||
module.exports = Storage |
||||
|
@ -1,7 +1,7 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
require('es6-shim'); |
||||
var app = require('./app.js'); |
||||
var $ = require('jquery'); |
||||
require('es6-shim') |
||||
var app = require('./app.js') |
||||
var $ = require('jquery') |
||||
|
||||
$(document).ready(function () { app.run(); }); |
||||
$(document).ready(function () { app.run() }) |
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,23 +1,23 @@ |
||||
module.exports = { |
||||
checkCompiledContracts: function (browser, compiled, callback) { |
||||
browser.execute(function () { |
||||
var contracts = document.querySelectorAll('.udapp .contract'); |
||||
var ret = []; |
||||
var contracts = document.querySelectorAll('.udapp .contract') |
||||
var ret = [] |
||||
for (var k in contracts) { |
||||
var el = contracts[k]; |
||||
var el = contracts[k] |
||||
if (el.querySelector) { |
||||
ret.push({ |
||||
name: el.querySelector('.title').innerText.replace(el.querySelector('.size').innerText, '').replace(/(\t)|(\r)|(\n)/g, '') // IE/firefox add \r\n
|
||||
}); |
||||
}) |
||||
} |
||||
} |
||||
return ret; |
||||
return ret |
||||
}, [''], function (result) { |
||||
browser.assert.equal(result.value.length, compiled.length); |
||||
browser.assert.equal(result.value.length, compiled.length) |
||||
result.value.map(function (item, i) { |
||||
browser.assert.equal(item.name, compiled[i]); |
||||
}); |
||||
callback(); |
||||
}); |
||||
browser.assert.equal(item.name, compiled[i]) |
||||
}) |
||||
callback() |
||||
}) |
||||
} |
||||
}; |
||||
} |
||||
|
@ -1,19 +1,19 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var test = require('tape'); |
||||
var test = require('tape') |
||||
|
||||
var Compiler = require('../src/app/compiler'); |
||||
var EventManager = require('../src/lib/eventManager'); |
||||
var Compiler = require('../src/app/compiler') |
||||
var EventManager = require('../src/lib/eventManager') |
||||
|
||||
test('compiler.compile smoke', function (t) { |
||||
t.plan(1); |
||||
t.plan(1) |
||||
|
||||
var noop = function () {}; |
||||
var getCacheFile = function () { return 'fakeCacheFile'; }; |
||||
var fakeEditor = {onChangeSetup: noop, clearAnnotations: noop, getValue: noop, setCacheFileContent: noop, getCacheFile: getCacheFile}; |
||||
var fakeQueryParams = {get: function () { return {}; }}; |
||||
var compiler = new Compiler(fakeEditor, fakeQueryParams, null, null, new EventManager()); |
||||
compiler.setCompileJSON(noop); |
||||
compiler.compile(); |
||||
t.ok(compiler); |
||||
}); |
||||
var noop = function () {} |
||||
var getCacheFile = function () { return 'fakeCacheFile' } |
||||
var fakeEditor = {onChangeSetup: noop, clearAnnotations: noop, getValue: noop, setCacheFileContent: noop, getCacheFile: getCacheFile} |
||||
var fakeQueryParams = {get: function () { return {} }} |
||||
var compiler = new Compiler(fakeEditor, fakeQueryParams, null, null, new EventManager()) |
||||
compiler.setCompileJSON(noop) |
||||
compiler.compile() |
||||
t.ok(compiler) |
||||
}) |
||||
|
@ -1,71 +1,70 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var test = require('tape'); |
||||
var test = require('tape') |
||||
|
||||
var GistHandler = require('../src/app/gist-handler'); |
||||
var GistHandler = require('../src/app/gist-handler') |
||||
|
||||
test('gistHandler.handleLoad with no gist param', function (t) { |
||||
t.plan(1); |
||||
t.plan(1) |
||||
|
||||
var gistHandler = new GistHandler({}); |
||||
var gistHandler = new GistHandler({}) |
||||
|
||||
var params = {}; |
||||
var result = gistHandler.handleLoad(params, null); |
||||
var params = {} |
||||
var result = gistHandler.handleLoad(params, null) |
||||
|
||||
t.equal(result, false); |
||||
}); |
||||
t.equal(result, false) |
||||
}) |
||||
|
||||
test('gistHandler.handleLoad with blank gist param, and invalid user input', function (t) { |
||||
t.plan(3); |
||||
t.plan(3) |
||||
|
||||
var fakeWindow = {prompt: function (message) { |
||||
t.ok(message); |
||||
t.ok(message.match(/gist/i)); |
||||
return 'invalid'; |
||||
}}; |
||||
t.ok(message) |
||||
t.ok(message.match(/gist/i)) |
||||
return 'invalid' |
||||
}} |
||||
|
||||
var gistHandler = new GistHandler(fakeWindow); |
||||
var gistHandler = new GistHandler(fakeWindow) |
||||
|
||||
var params = {'gist': ''}; |
||||
var result = gistHandler.handleLoad(params, null); |
||||
var params = {'gist': ''} |
||||
var result = gistHandler.handleLoad(params, null) |
||||
|
||||
t.equal(result, false); |
||||
}); |
||||
t.equal(result, false) |
||||
}) |
||||
|
||||
test('gistHandler.handleLoad with blank gist param, and valid user input', function (t) { |
||||
t.plan(4); |
||||
t.plan(4) |
||||
|
||||
var fakeWindow = {prompt: function (message) { |
||||
t.ok(message); |
||||
t.ok(message.match(/gist/i)); |
||||
return 'Beef1234'; |
||||
}}; |
||||
t.ok(message) |
||||
t.ok(message.match(/gist/i)) |
||||
return 'Beef1234' |
||||
}} |
||||
|
||||
var cb = function (gistId) { |
||||
t.equal(gistId, 'Beef1234'); |
||||
}; |
||||
t.equal(gistId, 'Beef1234') |
||||
} |
||||
|
||||
var gistHandler = new GistHandler(fakeWindow); |
||||
var gistHandler = new GistHandler(fakeWindow) |
||||
|
||||
var params = {'gist': ''}; |
||||
var result = gistHandler.handleLoad(params, cb); |
||||
var params = {'gist': ''} |
||||
var result = gistHandler.handleLoad(params, cb) |
||||
|
||||
t.equal(result, true); |
||||
}); |
||||
t.equal(result, true) |
||||
}) |
||||
|
||||
test('gistHandler.handleLoad with gist param', function (t) { |
||||
t.plan(2); |
||||
t.plan(2) |
||||
|
||||
var gistHandler = new GistHandler({}); |
||||
var gistHandler = new GistHandler({}) |
||||
|
||||
var params = {'gist': 'abc'}; |
||||
var params = {'gist': 'abc'} |
||||
|
||||
var cb = function (gistId) { |
||||
t.equal(gistId, 'abc'); |
||||
}; |
||||
t.equal(gistId, 'abc') |
||||
} |
||||
|
||||
var result = gistHandler.handleLoad(params, cb); |
||||
|
||||
t.equal(result, true); |
||||
}); |
||||
var result = gistHandler.handleLoad(params, cb) |
||||
|
||||
t.equal(result, true) |
||||
}) |
||||
|
@ -1,5 +1,5 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
require('./compiler-test'); |
||||
require('./gist-handler-test'); |
||||
require('./query-params-test'); |
||||
require('./compiler-test') |
||||
require('./gist-handler-test') |
||||
require('./query-params-test') |
||||
|
@ -1,23 +1,23 @@ |
||||
'use strict'; |
||||
'use strict' |
||||
|
||||
var test = require('tape'); |
||||
var test = require('tape') |
||||
|
||||
var QueryParams = require('../src/app/query-params'); |
||||
var QueryParams = require('../src/app/query-params') |
||||
|
||||
test('queryParams.get', function (t) { |
||||
t.plan(2); |
||||
t.plan(2) |
||||
|
||||
var fakeWindow = {location: {hash: '#wat=sup&foo=bar', search: ''}}; |
||||
var params = new QueryParams(fakeWindow).get(); |
||||
t.equal(params.wat, 'sup'); |
||||
t.equal(params.foo, 'bar'); |
||||
}); |
||||
var fakeWindow = {location: {hash: '#wat=sup&foo=bar', search: ''}} |
||||
var params = new QueryParams(fakeWindow).get() |
||||
t.equal(params.wat, 'sup') |
||||
t.equal(params.foo, 'bar') |
||||
}) |
||||
|
||||
test('queryParams.update', function (t) { |
||||
t.plan(1); |
||||
t.plan(1) |
||||
|
||||
var fakeWindow = {location: {hash: '#wat=sup', search: ''}}; |
||||
var qp = new QueryParams(fakeWindow); |
||||
qp.update({foo: 'bar'}); |
||||
t.equal(fakeWindow.location.hash, '#wat=sup&foo=bar'); |
||||
}); |
||||
var fakeWindow = {location: {hash: '#wat=sup', search: ''}} |
||||
var qp = new QueryParams(fakeWindow) |
||||
qp.update({foo: 'bar'}) |
||||
t.equal(fakeWindow.location.hash, '#wat=sup&foo=bar') |
||||
}) |
||||
|
Loading…
Reference in new issue