parent
d3537b3196
commit
2aed87988b
@ -0,0 +1,201 @@ |
||||
'use strict' |
||||
var async = require('async') |
||||
var EventManager = require('ethereum-remix').lib.EventManager |
||||
|
||||
class SystemFiles { |
||||
constructor (remixd) { |
||||
this.event = new EventManager() |
||||
this.remixd = remixd |
||||
this.files = null |
||||
this.filesContent = {} |
||||
this.filesTree = null |
||||
this.type = 'localhost' |
||||
this.error = { |
||||
'EEXIST': 'File already exists' |
||||
} |
||||
this.remixd.event.register('notified', (data) => { |
||||
if (data.scope === 'systemfiles') { |
||||
if (data.name === 'created') { |
||||
this.init(() => { |
||||
this.event.trigger('fileAdded', [this.type + '/' + data.value.path, data.value.isReadOnly, data.value.isFolder]) |
||||
}) |
||||
} else if (data.name === 'removed') { |
||||
this.init(() => { |
||||
this.event.trigger('fileRemoved', [this.type + '/' + data.value.path]) |
||||
}) |
||||
} else if (data.name === 'changed') { |
||||
this.remixd.call('systemfiles', 'get', {path: data.value}, (error, content) => { |
||||
if (error) { |
||||
console.log(error) |
||||
} else { |
||||
var path = this.type + '/' + data.value |
||||
this.filesContent[path] = content |
||||
this.event.trigger('fileExternallyChanged', [path, content]) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
|
||||
close (cb) { |
||||
this.remixd.close() |
||||
this.files = null |
||||
this.filesTree = null |
||||
cb() |
||||
} |
||||
|
||||
init (cb) { |
||||
this.remixd.call('systemfiles', 'list', {}, (error, filesList) => { |
||||
if (error) { |
||||
cb(error) |
||||
} else { |
||||
this.files = {} |
||||
for (var k in filesList) { |
||||
this.files[this.type + '/' + k] = filesList[k] |
||||
} |
||||
listAsTree(this, this.files, (error, tree) => { |
||||
this.filesTree = tree |
||||
cb(error) |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
exists (path) { |
||||
if (!this.files) return false |
||||
return this.files[path] !== undefined |
||||
} |
||||
|
||||
get (path, cb) { |
||||
var unprefixedpath = this.removePrefix(path) |
||||
this.remixd.call('systemfiles', 'get', {path: unprefixedpath}, (error, content) => { |
||||
if (!error) { |
||||
this.filesContent[path] = content |
||||
cb(error, content) |
||||
} else { |
||||
// display the last known content.
|
||||
// TODO should perhaps better warn the user that the file is not synced.
|
||||
cb(null, this.filesContent[path]) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
set (path, content, cb) { |
||||
var unprefixedpath = this.removePrefix(path) |
||||
this.remixd.call('systemfiles', 'set', {path: unprefixedpath, content: content}, (error, result) => { |
||||
if (cb) cb(error, result) |
||||
var path = this.type + '/' + unprefixedpath |
||||
this.filesContent[path] |
||||
this.event.trigger('fileChanged', [path]) |
||||
}) |
||||
return true |
||||
} |
||||
|
||||
addReadOnly (path, content) { |
||||
return false |
||||
} |
||||
|
||||
isReadOnly (path) { |
||||
if (this.files) return this.files[path] |
||||
return true |
||||
} |
||||
|
||||
remove (path) { |
||||
var unprefixedpath = this.removePrefix(path) |
||||
this.remixd.call('systemfiles', 'remove', {path: unprefixedpath}, (error, result) => { |
||||
if (error) console.log(error) |
||||
var path = this.type + '/' + unprefixedpath |
||||
delete this.filesContent[path] |
||||
this.init(() => { |
||||
this.event.trigger('fileRemoved', [path]) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
rename (oldPath, newPath, isFolder) { |
||||
var unprefixedoldPath = this.removePrefix(oldPath) |
||||
var unprefixednewPath = this.removePrefix(newPath) |
||||
this.remixd.call('systemfiles', 'rename', {oldPath: unprefixedoldPath, newPath: unprefixednewPath}, (error, result) => { |
||||
if (error) { |
||||
console.log(error) |
||||
if (this.error[error.code]) error = this.error[error.code] |
||||
this.event.trigger('fileRenamedError', [this.error[error.code]]) |
||||
} else { |
||||
var newPath = this.type + '/' + unprefixednewPath |
||||
var oldPath = this.type + '/' + unprefixedoldPath |
||||
this.filesContent[newPath] = this.filesContent[oldPath] |
||||
delete this.filesContent[oldPath] |
||||
this.init(() => { |
||||
this.event.trigger('fileRenamed', [oldPath, newPath, isFolder]) |
||||
}) |
||||
} |
||||
}) |
||||
return true |
||||
} |
||||
|
||||
list () { |
||||
return this.files |
||||
} |
||||
|
||||
listAsTree () { |
||||
return this.filesTree |
||||
} |
||||
|
||||
removePrefix (path) { |
||||
return path.indexOf(this.type + '/') === 0 ? path.replace(this.type + '/', '') : path |
||||
} |
||||
} |
||||
|
||||
//
|
||||
// Tree model for files
|
||||
// {
|
||||
// 'a': { }, // empty directory 'a'
|
||||
// 'b': {
|
||||
// 'c': {}, // empty directory 'b/c'
|
||||
// 'd': { '/readonly': true, '/content': 'Hello World' } // files 'b/c/d'
|
||||
// 'e': { '/readonly': false, '/path': 'b/c/d' } // symlink to 'b/c/d'
|
||||
// 'f': { '/readonly': false, '/content': '<executable>', '/mode': 0755 }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
function listAsTree (self, filesList, callback) { |
||||
function hashmapize (obj, path, val) { |
||||
var nodes = path.split('/') |
||||
var i = 0 |
||||
|
||||
for (; i < nodes.length - 1; i++) { |
||||
var node = nodes[i] |
||||
if (obj[node] === undefined) { |
||||
obj[node] = {} |
||||
} |
||||
obj = obj[node] |
||||
} |
||||
|
||||
obj[nodes[i]] = val |
||||
} |
||||
|
||||
var tree = {} |
||||
|
||||
// This does not include '.remix.config', because it is filtered
|
||||
// inside list().
|
||||
async.eachSeries(Object.keys(filesList), function (path, cb) { |
||||
self.get(path, (error, content) => { |
||||
if (error) { |
||||
console.log(error) |
||||
cb(error) |
||||
} else { |
||||
self.filesContent[path] = content |
||||
hashmapize(tree, path, { |
||||
'/readonly': filesList[path], |
||||
'/content': content |
||||
}) |
||||
cb() |
||||
} |
||||
}) |
||||
}, (error) => { |
||||
callback(error, tree) |
||||
}) |
||||
} |
||||
|
||||
module.exports = SystemFiles |
@ -0,0 +1,52 @@ |
||||
module.exports = (title, content, okFn, cancelFn) => { |
||||
var modal = document.querySelector('.modal-body') |
||||
var modaltitle = document.querySelector('.modal-header h2') |
||||
|
||||
modaltitle.innerHTML = ' - ' |
||||
if (title) modaltitle.innerHTML = title |
||||
|
||||
modal.innerHTML = '' |
||||
if (content) modal.appendChild(content) |
||||
|
||||
var container = document.querySelector('.modal') |
||||
container.style.display = container.style.display === 'block' ? hide() : show() |
||||
|
||||
function ok () { |
||||
hide() |
||||
if (okFn) okFn() |
||||
removeEventListener() |
||||
} |
||||
|
||||
function cancel () { |
||||
hide() |
||||
if (cancelFn) cancelFn() |
||||
removeEventListener() |
||||
} |
||||
|
||||
function blur (event) { |
||||
if (event.target === container) { |
||||
cancel() |
||||
} |
||||
} |
||||
|
||||
window.onclick = (event) => { |
||||
console.log('clicj windo') |
||||
blur(event) |
||||
} |
||||
|
||||
function hide () { |
||||
container.style.display = 'none' |
||||
} |
||||
|
||||
function show () { |
||||
container.style.display = 'block' |
||||
} |
||||
|
||||
function removeEventListener () { |
||||
document.getElementById('modal-footer-ok').removeEventListener('click', ok) |
||||
document.getElementById('modal-footer-cancel').removeEventListener('click', cancel) |
||||
} |
||||
|
||||
document.getElementById('modal-footer-ok').addEventListener('click', ok) |
||||
document.getElementById('modal-footer-cancel').addEventListener('click', cancel) |
||||
} |
@ -0,0 +1,113 @@ |
||||
'use strict' |
||||
var EventManager = require('ethereum-remix').lib.EventManager |
||||
|
||||
class Remixd { |
||||
constructor () { |
||||
this.event = new EventManager() |
||||
this.callbacks = {} |
||||
this.callid = 0 |
||||
this.socket = null |
||||
this.connected = false |
||||
} |
||||
|
||||
online () { |
||||
return this.socket !== null |
||||
} |
||||
|
||||
close () { |
||||
if (this.socket) { |
||||
this.socket.close() |
||||
this.socket = null |
||||
} |
||||
} |
||||
|
||||
start (cb) { |
||||
if (this.socket) { |
||||
try { |
||||
this.socket.close() |
||||
} catch (e) {} |
||||
} |
||||
this.event.trigger('connecting', []) |
||||
this.socket = new WebSocket('ws://localhost:65520', 'echo-protocol') // eslint-disable-line
|
||||
|
||||
this.socket.addEventListener('open', (event) => { |
||||
this.connected = true |
||||
this.event.trigger('connected', [event]) |
||||
cb() |
||||
}) |
||||
|
||||
this.socket.addEventListener('message', (event) => { |
||||
var data = JSON.parse(event.data) |
||||
if (data.type === 'reply') { |
||||
if (this.callbacks[data.id]) { |
||||
this.callbacks[data.id](data.error, data.result) |
||||
delete this.callbacks[data.id] |
||||
} |
||||
this.event.trigger('replied', [data]) |
||||
} else if (data.type === 'notification') { |
||||
this.event.trigger('notified', [data]) |
||||
} |
||||
}) |
||||
|
||||
this.socket.addEventListener('error', (event) => { |
||||
this.errored(event) |
||||
cb(event) |
||||
}) |
||||
|
||||
this.socket.addEventListener('close', (event) => { |
||||
if (event.wasClean) { |
||||
this.connected = false |
||||
this.event.trigger('closed', [event]) |
||||
} else { |
||||
this.errored(event) |
||||
} |
||||
this.socket = null |
||||
}) |
||||
} |
||||
|
||||
errored (event) { |
||||
if (this.connected) { |
||||
alert('connection to Remixd lost!.') // eslint-disable-line
|
||||
} |
||||
this.connected = false |
||||
this.socket = null |
||||
this.event.trigger('errored', [event]) |
||||
} |
||||
|
||||
call (service, fn, args, callback) { |
||||
this.ensureSocket((error) => { |
||||
if (error) return callback(error) |
||||
if (this.socket && this.socket.readyState === this.socket.OPEN) { |
||||
var data = this.format(service, fn, args) |
||||
this.callbacks[data.id] = callback |
||||
this.socket.send(JSON.stringify(data)) |
||||
} else { |
||||
callback('Socket not ready. state:' + this.socket.readyState) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
ensureSocket (cb) { |
||||
if (this.socket) return cb(null, this.socket) |
||||
this.start((error) => { |
||||
if (error) { |
||||
cb(error) |
||||
} else { |
||||
cb(null, this.socket) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
format (service, fn, args) { |
||||
var data = { |
||||
id: this.callid, |
||||
service: service, |
||||
fn: fn, |
||||
args: args |
||||
} |
||||
this.callid++ |
||||
return data |
||||
} |
||||
} |
||||
|
||||
module.exports = Remixd |
Loading…
Reference in new issue