Update the Console dapp (#5602)
* Init Console Dapp structure * Watches and status * First REPL and display * Attaching console * Selectable autocomplete * working console // Display objects nicely * Multilines in Console Dapps * Better UI * Eval on window object * Save console gistory * Adding views * Add settings to the console dapp * Add / remove Watches * Add Snippets Cmponent * Semi Working Snippets * Working Snippets * Adding CodeMirror features * Removing old Console * Add Static folder
This commit is contained in:
parent
3ff72794e5
commit
84cab18120
@ -196,11 +196,13 @@
|
||||
"react": "15.4.2",
|
||||
"react-ace": "4.1.0",
|
||||
"react-addons-css-transition-group": "15.4.2",
|
||||
"react-codemirror": "^0.3.0",
|
||||
"react-copy-to-clipboard": "4.2.3",
|
||||
"react-dom": "15.4.2",
|
||||
"react-dropzone": "3.7.3",
|
||||
"react-element-to-jsx-string": "6.0.0",
|
||||
"react-event-listener": "0.4.1",
|
||||
"react-inspector": "paritytech/react-inspector",
|
||||
"react-intl": "2.1.5",
|
||||
"react-markdown": "2.4.4",
|
||||
"react-portal": "3.0.0",
|
||||
|
59
js/src/dapps/console.js
Normal file
59
js/src/dapps/console.js
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
||||
import 'codemirror/addon/dialog/dialog';
|
||||
import 'codemirror/addon/dialog/dialog.css';
|
||||
import 'codemirror/addon/hint/javascript-hint';
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
import 'codemirror/addon/hint/show-hint.css';
|
||||
import 'codemirror/addon/search/match-highlighter';
|
||||
import 'codemirror/addon/search/search';
|
||||
import 'codemirror/addon/search/searchcursor';
|
||||
import 'codemirror/keymap/sublime';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import 'codemirror/mode/javascript/javascript';
|
||||
// Custom codemirror style
|
||||
import './console/codemirror.css';
|
||||
|
||||
import Application from './console/Application';
|
||||
|
||||
import '../../assets/fonts/Roboto/font.css';
|
||||
import '../../assets/fonts/RobotoMono/font.css';
|
||||
import './style.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./console/Application/index.js', () => {
|
||||
require('./console/Application/index.js');
|
||||
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
});
|
||||
}
|
65
js/src/dapps/console/Application/application.css
Normal file
65
js/src/dapps/console/Application/application.css
Normal file
@ -0,0 +1,65 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 11px;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
textarea,
|
||||
input {
|
||||
font-family: dejavu sans mono, monospace;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
font-family: dejavu sans mono, monospace;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.view {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.eval {
|
||||
flex: 0 1 auto;
|
||||
font-family: dejavu sans mono, monospace;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.input {
|
||||
border-top: 1px solid #eee;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.status {
|
||||
flex: 0 0 auto;
|
||||
font-family: dejavu sans mono, monospace;
|
||||
}
|
94
js/src/dapps/console/Application/application.js
Normal file
94
js/src/dapps/console/Application/application.js
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { api } from '../parity';
|
||||
|
||||
import Console from '../Console';
|
||||
import Header from '../Header';
|
||||
import Input from '../Input';
|
||||
import Settings from '../Settings';
|
||||
import Snippets from '../Snippets';
|
||||
import Watches from '../Watches';
|
||||
|
||||
import ApplicationStore from './application.store';
|
||||
import WatchesStore from '../Watches/watches.store';
|
||||
|
||||
import styles from './application.css';
|
||||
|
||||
@observer
|
||||
export default class Application extends Component {
|
||||
application = ApplicationStore.get();
|
||||
watches = WatchesStore.get();
|
||||
|
||||
componentWillMount () {
|
||||
this.watches.add('time', () => new Date());
|
||||
this.watches.add('blockNumber', api.eth.blockNumber, api);
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className={ styles.app }>
|
||||
<div className={ styles.header }>
|
||||
<Header />
|
||||
</div>
|
||||
|
||||
{ this.renderView() }
|
||||
|
||||
<div className={ styles.status }>
|
||||
<Watches />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderView () {
|
||||
const { view } = this.application;
|
||||
|
||||
if (view === 'console') {
|
||||
return (
|
||||
<div className={ styles.view }>
|
||||
<div className={ styles.eval }>
|
||||
<Console />
|
||||
</div>
|
||||
<div className={ styles.input }>
|
||||
<Input />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (view === 'settings') {
|
||||
return (
|
||||
<div className={ styles.view }>
|
||||
<Settings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (view === 'snippets') {
|
||||
return (
|
||||
<div className={ styles.view }>
|
||||
<Snippets />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
42
js/src/dapps/console/Application/application.store.js
Normal file
42
js/src/dapps/console/Application/application.store.js
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, observable } from 'mobx';
|
||||
|
||||
let instance;
|
||||
|
||||
export default class ApplicationStore {
|
||||
@observable view = this.views[0].id;
|
||||
|
||||
views = [
|
||||
{ label: 'Console', id: 'console' },
|
||||
{ label: 'Snippets', id: 'snippets' },
|
||||
{ label: 'Settings', id: 'settings' }
|
||||
];
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new ApplicationStore();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@action
|
||||
setView (view) {
|
||||
this.view = view;
|
||||
}
|
||||
}
|
17
js/src/dapps/console/Application/index.js
Normal file
17
js/src/dapps/console/Application/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './application';
|
55
js/src/dapps/console/Autocomplete/autocomplete.css
Normal file
55
js/src/dapps/console/Autocomplete/autocomplete.css
Normal file
@ -0,0 +1,55 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
background: #f8f8f8;
|
||||
box-shadow: 0 0.125em 0.25em rgba(0, 0, 0, 0.5);
|
||||
font-family: dejavu sans mono, monospace;
|
||||
left: 20px;
|
||||
position: absolute;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.item {
|
||||
background-color: white;
|
||||
padding: 0.25em 0.25em 0.25em 0.35em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
&.selected {
|
||||
background-color: rgb(64, 115, 244);
|
||||
|
||||
&,
|
||||
.proto {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&:hover:not(.selected) {
|
||||
background-color: rgb(230, 236, 255);
|
||||
}
|
||||
|
||||
.proto {
|
||||
color: gray;
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
96
js/src/dapps/console/Autocomplete/autocomplete.js
Normal file
96
js/src/dapps/console/Autocomplete/autocomplete.js
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import AutocompleteStore from './autocomplete.store';
|
||||
|
||||
import styles from './autocomplete.css';
|
||||
|
||||
@observer
|
||||
export default class Autocomplete extends Component {
|
||||
autocompleteStore = AutocompleteStore.get();
|
||||
|
||||
render () {
|
||||
if (!this.autocompleteStore.show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ styles.container }
|
||||
style={ this.autocompleteStore.position }
|
||||
>
|
||||
{ this.renderAutocompletes() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAutocompletes () {
|
||||
const { selected, values } = this.autocompleteStore;
|
||||
const displayedProto = {};
|
||||
|
||||
return values.map((autocomplete, index) => {
|
||||
const { name, prototypeName } = autocomplete;
|
||||
const onClick = () => this.handleClick(index);
|
||||
const setRef = (node) => this.setRef(index, node);
|
||||
|
||||
const proto = !displayedProto[prototypeName]
|
||||
? (
|
||||
<span className={ styles.proto }>
|
||||
{ prototypeName }
|
||||
</span>
|
||||
)
|
||||
: null;
|
||||
|
||||
if (!displayedProto[prototypeName]) {
|
||||
displayedProto[prototypeName] = true;
|
||||
}
|
||||
|
||||
const classes = [ styles.item ];
|
||||
|
||||
if (index === selected) {
|
||||
classes.push(styles.selected);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ classes.join(' ') }
|
||||
key={ index }
|
||||
onClick={ onClick }
|
||||
ref={ setRef }
|
||||
>
|
||||
<span>
|
||||
{ name }
|
||||
</span>
|
||||
{ proto }
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
handleClick = (index) => {
|
||||
this.autocompleteStore.select(index);
|
||||
};
|
||||
|
||||
setRef = (index, node) => {
|
||||
const element = ReactDOM.findDOMNode(node);
|
||||
|
||||
this.autocompleteStore.setElement(index, element);
|
||||
};
|
||||
}
|
234
js/src/dapps/console/Autocomplete/autocomplete.store.js
Normal file
234
js/src/dapps/console/Autocomplete/autocomplete.store.js
Normal file
@ -0,0 +1,234 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, observable } from 'mobx';
|
||||
|
||||
import { evaluate } from '../utils';
|
||||
|
||||
let instance;
|
||||
|
||||
export default class AutocompleteStore {
|
||||
@observable values = [];
|
||||
@observable position = {};
|
||||
@observable show = false;
|
||||
@observable selected = null;
|
||||
|
||||
elements = {};
|
||||
inputNode = null;
|
||||
lastObject = null;
|
||||
lastObjectPropertyNames = [];
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new AutocompleteStore();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
get hasSelected () {
|
||||
return this.selected !== null;
|
||||
}
|
||||
|
||||
clearCache () {
|
||||
this.lastObject = null;
|
||||
this.lastObjectPropertyNames = null;
|
||||
}
|
||||
|
||||
@action
|
||||
focus (offset = 1) {
|
||||
if (this.values.length === 0) {
|
||||
this.selected = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = this.selected === null
|
||||
? (
|
||||
offset === 1
|
||||
? 0
|
||||
: this.values.length - 1
|
||||
)
|
||||
: (this.values.length + this.selected + offset) % (this.values.length);
|
||||
|
||||
if (this.isVisible(this.selected)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const element = this.elements[this.selected];
|
||||
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.scrollIntoView(offset === -1);
|
||||
}
|
||||
|
||||
focusOnInput () {
|
||||
if (!this.inputNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inputNode.focus();
|
||||
}
|
||||
|
||||
@action
|
||||
hide () {
|
||||
this.show = false;
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
isVisible (index) {
|
||||
const element = this.elements[index];
|
||||
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const eBoundings = element.getBoundingClientRect();
|
||||
const pBoundings = element.parentElement.getBoundingClientRect();
|
||||
|
||||
if (eBoundings.top < pBoundings.top || eBoundings.bottom > pBoundings.bottom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
select (inputStore, _index = this.selected) {
|
||||
const index = _index === null
|
||||
? 0
|
||||
: _index;
|
||||
|
||||
if (!this.values[index]) {
|
||||
console.warn(`autocomplete::select has been called on AutocompleteStore with wrong value ${index}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const { name } = this.values[index];
|
||||
const { input } = inputStore;
|
||||
const objects = input.split('.');
|
||||
|
||||
objects[objects.length - 1] = name;
|
||||
const nextInput = objects.join('.');
|
||||
|
||||
this.hide();
|
||||
this.focusOnInput();
|
||||
return inputStore.updateInput(nextInput, false);
|
||||
}
|
||||
|
||||
setElement (index, element) {
|
||||
this.elements[index] = element;
|
||||
}
|
||||
|
||||
setInputNode (node) {
|
||||
this.inputNode = node;
|
||||
}
|
||||
|
||||
@action
|
||||
setPosition () {
|
||||
if (!this.inputNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inputBoundings = this.inputNode.getBoundingClientRect();
|
||||
const bodyBoundings = document.body.getBoundingClientRect();
|
||||
|
||||
// display on bottom of input
|
||||
if (inputBoundings.top < bodyBoundings.height / 2) {
|
||||
const nextPosition = {
|
||||
top: 20
|
||||
};
|
||||
|
||||
this.position = nextPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
// display on top of input
|
||||
const nextPosition = {
|
||||
bottom: inputBoundings.height
|
||||
};
|
||||
|
||||
this.position = nextPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
@action
|
||||
setValues (values) {
|
||||
this.values = values;
|
||||
this.selected = null;
|
||||
const show = values.length > 0;
|
||||
|
||||
// Reveal autocomplete
|
||||
if (!this.show && show) {
|
||||
this.setPosition();
|
||||
}
|
||||
|
||||
this.show = show;
|
||||
}
|
||||
|
||||
update (input) {
|
||||
if (input.length === 0) {
|
||||
return this.setValues([]);
|
||||
}
|
||||
|
||||
const objects = input.split('.');
|
||||
const suffix = objects.pop().toLowerCase();
|
||||
const prefix = objects.join('.');
|
||||
const object = prefix.length > 0
|
||||
? prefix
|
||||
: 'window';
|
||||
|
||||
if (object !== this.lastObject) {
|
||||
const evalResult = evaluate(object);
|
||||
|
||||
if (evalResult.error) {
|
||||
this.lastObjectProperties = [];
|
||||
} else {
|
||||
this.lastObjectProperties = getAllProperties(evalResult.result);
|
||||
}
|
||||
|
||||
this.lastObject = object;
|
||||
}
|
||||
|
||||
const autocompletes = this.lastObjectProperties.filter((property) => {
|
||||
return property.name.toLowerCase().includes(suffix);
|
||||
});
|
||||
|
||||
return this.setValues(autocompletes);
|
||||
}
|
||||
}
|
||||
|
||||
function getAllProperties (object) {
|
||||
const propertyNames = {};
|
||||
|
||||
while (object) {
|
||||
const prototypeName = object && object.constructor && object.constructor.name || '';
|
||||
|
||||
Object.getOwnPropertyNames(object)
|
||||
.sort()
|
||||
.forEach((name) => {
|
||||
if (Object.prototype.hasOwnProperty.call(propertyNames, name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
propertyNames[name] = { name, prototypeName };
|
||||
});
|
||||
|
||||
object = Object.getPrototypeOf(object);
|
||||
}
|
||||
|
||||
return Object.values(propertyNames);
|
||||
}
|
17
js/src/dapps/console/Autocomplete/index.js
Normal file
17
js/src/dapps/console/Autocomplete/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './autocomplete';
|
58
js/src/dapps/console/Console/console.css
Normal file
58
js/src/dapps/console/Console/console.css
Normal file
@ -0,0 +1,58 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.result {
|
||||
border-top: 1px solid #eee;
|
||||
display: flex;
|
||||
font-family: dejavu sans mono, monospace;
|
||||
padding: 0.35em 0.25em;
|
||||
|
||||
&.error {
|
||||
background-color: hsl(0, 100%, 97%);
|
||||
|
||||
.text {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
&.warn {
|
||||
background-color: hsl(50, 100%, 95%);
|
||||
}
|
||||
}
|
||||
|
||||
.type {
|
||||
font-weight: bold !important;
|
||||
font-size: 8pt;
|
||||
padding: 0 0.5em 0 0.25em;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: gray;
|
||||
padding: 0 1em 0 0.5em;
|
||||
}
|
||||
|
||||
.token {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.text .token:not(:first-child) {
|
||||
margin-left: 0.5em;
|
||||
}
|
118
js/src/dapps/console/Console/console.js
Normal file
118
js/src/dapps/console/Console/console.js
Normal file
@ -0,0 +1,118 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { ObjectInspector } from 'react-inspector';
|
||||
|
||||
import ConsoleStore from './console.store';
|
||||
import SettingsStore from '../Settings/settings.store';
|
||||
|
||||
import styles from './console.css';
|
||||
|
||||
const ICONS = {
|
||||
debug: ' ',
|
||||
error: '✖',
|
||||
info: 'ℹ',
|
||||
input: '>',
|
||||
log: ' ',
|
||||
result: '<',
|
||||
warn: '⚠'
|
||||
};
|
||||
|
||||
@observer
|
||||
export default class Console extends Component {
|
||||
consoleStore = ConsoleStore.get();
|
||||
settingsStore = SettingsStore.get();
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div ref={ this.setRef }>
|
||||
{ this.renderResults() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderResults () {
|
||||
const { logs } = this.consoleStore;
|
||||
|
||||
return logs.map((data, index) => {
|
||||
const { type, timestamp } = data;
|
||||
const values = this.consoleStore.logValues[index];
|
||||
const classes = [ styles.result, styles[type] ];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ classes.join(' ') }
|
||||
key={ index }
|
||||
>
|
||||
<span
|
||||
className={ styles.type }
|
||||
dangerouslySetInnerHTML={ { __html: ICONS[type] || '' } }
|
||||
/>
|
||||
{ this.renderTimestamp(timestamp) }
|
||||
<span className={ styles.text }>
|
||||
{
|
||||
values.map((value, valueIndex) => (
|
||||
<span
|
||||
className={ styles.token }
|
||||
key={ valueIndex }
|
||||
>
|
||||
{ this.toString(value) }
|
||||
</span>
|
||||
))
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
renderTimestamp (timestamp) {
|
||||
const { displayTimestamps } = this.settingsStore;
|
||||
|
||||
if (!displayTimestamps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={ styles.time }>
|
||||
{ new Date(timestamp).toISOString().slice(11, 23) }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
setRef = (node) => {
|
||||
const element = ReactDOM.findDOMNode(node);
|
||||
|
||||
this.consoleStore.setNode(element);
|
||||
};
|
||||
|
||||
toString (value) {
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value instanceof Error) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
return (
|
||||
<ObjectInspector data={ value } />
|
||||
);
|
||||
}
|
||||
}
|
126
js/src/dapps/console/Console/console.store.js
Normal file
126
js/src/dapps/console/Console/console.store.js
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, observable } from 'mobx';
|
||||
|
||||
import AutocompleteStore from '../Autocomplete/autocomplete.store';
|
||||
import { evaluate } from '../utils';
|
||||
|
||||
let instance;
|
||||
|
||||
export default class ConsoleStore {
|
||||
@observable logs = [];
|
||||
|
||||
autocompleteStore = AutocompleteStore.get();
|
||||
logValues = [];
|
||||
node = null;
|
||||
|
||||
constructor () {
|
||||
this.attachConsole();
|
||||
}
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new ConsoleStore();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
attachConsole () {
|
||||
['debug', 'error', 'info', 'log', 'warn'].forEach((level) => {
|
||||
const old = window.console[level].bind(window.console);
|
||||
|
||||
window.console[level] = (...args) => {
|
||||
old(...args);
|
||||
this.log({ type: level, values: args });
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
clear () {
|
||||
this.logs = [];
|
||||
this.logValues = [];
|
||||
}
|
||||
|
||||
evaluate (input) {
|
||||
this.log({ type: 'input', value: input });
|
||||
|
||||
setTimeout(() => {
|
||||
const { result, error } = evaluate(input);
|
||||
let value = error || result;
|
||||
const type = error
|
||||
? 'error'
|
||||
: 'result';
|
||||
|
||||
if (typeof value === 'string') {
|
||||
value = `"${value}"`;
|
||||
}
|
||||
|
||||
if (value && typeof value === 'object' && typeof value.then === 'function') {
|
||||
return value
|
||||
.then((result) => {
|
||||
this.log({ type: 'result', value: result });
|
||||
})
|
||||
.catch((error) => {
|
||||
this.log({ type: 'error', value: error });
|
||||
});
|
||||
}
|
||||
|
||||
this.log({ type, value });
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
log ({ type, value, values }) {
|
||||
this.logs.push({
|
||||
type,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
if (values) {
|
||||
this.logValues.push(values);
|
||||
} else {
|
||||
this.logValues.push([ value ]);
|
||||
}
|
||||
|
||||
this.autocompleteStore.setPosition();
|
||||
this.scroll();
|
||||
}
|
||||
|
||||
setNode (node) {
|
||||
this.node = node;
|
||||
this.scroll();
|
||||
}
|
||||
|
||||
scroll () {
|
||||
if (!this.node) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.node.children.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Scroll to the last child
|
||||
this.node
|
||||
.children[this.node.children.length - 1]
|
||||
.scrollIntoView(false);
|
||||
}, 50);
|
||||
}
|
||||
}
|
17
js/src/dapps/console/Console/index.js
Normal file
17
js/src/dapps/console/Console/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './console';
|
51
js/src/dapps/console/Header/header.css
Normal file
51
js/src/dapps/console/Header/header.css
Normal file
@ -0,0 +1,51 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
background-color: #f3f3f3;
|
||||
border-bottom: 1px solid #ccc;
|
||||
font-size: 12px;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tab {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
color: #333;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
height: 24px;
|
||||
line-height: 15px;
|
||||
margin-top: 2px;
|
||||
padding: 2px 6px 2px 4px;
|
||||
|
||||
&:hover,
|
||||
&.active:hover {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
65
js/src/dapps/console/Header/header.js
Normal file
65
js/src/dapps/console/Header/header.js
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import ApplicationStore from '../Application/application.store';
|
||||
|
||||
import styles from './header.css';
|
||||
|
||||
@observer
|
||||
export default class Header extends Component {
|
||||
application = ApplicationStore.get();
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<div className={ styles.tabs }>
|
||||
{ this.renderTabs() }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTabs () {
|
||||
const { view } = this.application;
|
||||
|
||||
return this.application.views.map((tab) => {
|
||||
const { label, id } = tab;
|
||||
const classes = [ styles.tab ];
|
||||
const onClick = () => this.handleClickTab(id);
|
||||
|
||||
if (id === view) {
|
||||
classes.push(styles.active);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ classes.join(' ') }
|
||||
key={ id }
|
||||
onClick={ onClick }
|
||||
>
|
||||
{ label }
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
handleClickTab = (id) => {
|
||||
this.application.setView(id);
|
||||
};
|
||||
}
|
17
js/src/dapps/console/Header/index.js
Normal file
17
js/src/dapps/console/Header/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './header';
|
17
js/src/dapps/console/Input/index.js
Normal file
17
js/src/dapps/console/Input/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './input';
|
46
js/src/dapps/console/Input/input.css
Normal file
46
js/src/dapps/console/Input/input.css
Normal file
@ -0,0 +1,46 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.type {
|
||||
color: #59f;
|
||||
font-weight: bold !important;
|
||||
font-size: 11px;
|
||||
padding: 0 0.5em 0 0.25em;
|
||||
}
|
||||
|
||||
.inputContainer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.input {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: black;
|
||||
height: 100%;
|
||||
font-size: 11px;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
border-top: 1px solid lightgray;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
padding: 0.25em;
|
||||
position: relative;
|
||||
}
|
145
js/src/dapps/console/Input/input.js
Normal file
145
js/src/dapps/console/Input/input.js
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import keycode from 'keycode';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import Autocomplete from '../Autocomplete';
|
||||
|
||||
import AutocompleteStore from '../Autocomplete/autocomplete.store';
|
||||
import ConsoleStore from '../Console/console.store';
|
||||
import InputStore from './input.store';
|
||||
import SettingsStore from '../Settings/settings.store';
|
||||
|
||||
import styles from './input.css';
|
||||
|
||||
@observer
|
||||
export default class Input extends Component {
|
||||
autocompleteStore = AutocompleteStore.get();
|
||||
consoleStore = ConsoleStore.get();
|
||||
inputStore = InputStore.get();
|
||||
settingsStore = SettingsStore.get();
|
||||
|
||||
render () {
|
||||
const { input } = this.inputStore;
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<Autocomplete />
|
||||
<span className={ styles.type }>></span>
|
||||
<div className={ styles.inputContainer }>
|
||||
<textarea
|
||||
autoFocus
|
||||
className={ styles.input }
|
||||
onChange={ this.handleChange }
|
||||
onKeyDown={ this.handleKeyDown }
|
||||
ref={ this.setRef }
|
||||
rows={ input.split('\n').length }
|
||||
type='text'
|
||||
value={ input }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
handleChange = (event) => {
|
||||
const { value } = event.target;
|
||||
|
||||
this.inputStore.updateInput(value);
|
||||
};
|
||||
|
||||
handleKeyDown = (event) => {
|
||||
const { executeOnEnter } = this.settingsStore;
|
||||
const { input } = this.inputStore;
|
||||
const codeName = keycode(event);
|
||||
const multilines = input.split('\n').length > 1;
|
||||
|
||||
// Clear console with CTRL+L
|
||||
if (codeName === 'l' && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return this.consoleStore.clear();
|
||||
}
|
||||
|
||||
if (codeName === 'esc') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return this.autocompleteStore.hide();
|
||||
}
|
||||
|
||||
if (codeName === 'enter') {
|
||||
if (event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not execute on enter: execute on
|
||||
// enter + CTRL
|
||||
if (!executeOnEnter && !event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (this.autocompleteStore.hasSelected) {
|
||||
return this.autocompleteStore.select(this.inputStore);
|
||||
}
|
||||
|
||||
if (input.length > 0) {
|
||||
return this.inputStore.execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (codeName === 'up' && !multilines) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (this.autocompleteStore.show) {
|
||||
return this.autocompleteStore.focus(-1);
|
||||
}
|
||||
|
||||
return this.inputStore.selectHistory(-1);
|
||||
}
|
||||
|
||||
if (codeName === 'down' && !multilines) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (this.autocompleteStore.show) {
|
||||
return this.autocompleteStore.focus(1);
|
||||
}
|
||||
|
||||
return this.inputStore.selectHistory(1);
|
||||
}
|
||||
|
||||
if (codeName === 'left' && this.autocompleteStore.show) {
|
||||
return this.autocompleteStore.hide();
|
||||
}
|
||||
|
||||
if (codeName === 'right' && this.autocompleteStore.show) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return this.autocompleteStore.select(this.inputStore);
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (node) => {
|
||||
this.inputStore.setInputNode(ReactDOM.findDOMNode(node));
|
||||
};
|
||||
}
|
124
js/src/dapps/console/Input/input.store.js
Normal file
124
js/src/dapps/console/Input/input.store.js
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, observable } from 'mobx';
|
||||
import store from 'store';
|
||||
|
||||
import AutocompleteStore from '../Autocomplete/autocomplete.store';
|
||||
import ConsoleStore from '../Console/console.store';
|
||||
|
||||
const LS_HISTORY_KEY = '_console::history';
|
||||
const MAX_HISTORY_LINES = 5;
|
||||
|
||||
let instance;
|
||||
|
||||
export default class InputStore {
|
||||
@observable input = '';
|
||||
|
||||
autocompleteStore = AutocompleteStore.get();
|
||||
consoleStore = ConsoleStore.get();
|
||||
history = [];
|
||||
historyOffset = null;
|
||||
inputNode = null;
|
||||
lastInput = '';
|
||||
|
||||
constructor () {
|
||||
this.loadHistory();
|
||||
}
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new InputStore();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
setInputNode (node) {
|
||||
this.inputNode = node;
|
||||
this.autocompleteStore.setInputNode(node);
|
||||
}
|
||||
|
||||
@action
|
||||
updateInput (nextValue = '', updateAutocomplete = true) {
|
||||
this.input = nextValue;
|
||||
const multilines = nextValue.split('\n').length > 1;
|
||||
|
||||
if (updateAutocomplete && !multilines) {
|
||||
this.autocompleteStore.update(nextValue);
|
||||
}
|
||||
}
|
||||
|
||||
selectHistory (_offset) {
|
||||
// No history
|
||||
if (this.history.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.historyOffset === null) {
|
||||
// Can't go down if no history selected
|
||||
if (_offset === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.historyOffset = this.history.length - 1;
|
||||
this.lastInput = this.input;
|
||||
return this.updateInput(this.history[this.historyOffset], false);
|
||||
}
|
||||
|
||||
if (_offset === 1 && this.historyOffset === this.history.length - 1) {
|
||||
this.historyOffset = null;
|
||||
return this.updateInput(this.lastInput);
|
||||
}
|
||||
|
||||
this.historyOffset = Math.max(0, this.historyOffset + _offset);
|
||||
const nextInput = this.history[this.historyOffset];
|
||||
|
||||
this.updateInput(nextInput, false);
|
||||
}
|
||||
|
||||
execute () {
|
||||
const { input } = this;
|
||||
|
||||
this.pushToHistory(input);
|
||||
this.consoleStore.evaluate(input);
|
||||
this.updateInput('');
|
||||
this.historyOffset = null;
|
||||
this.autocompleteStore.clearCache();
|
||||
}
|
||||
|
||||
pushToHistory (input) {
|
||||
// Don't stack twice the same input in
|
||||
// history
|
||||
if (this.history[this.history.length - 1] !== input) {
|
||||
this.history.push(input);
|
||||
}
|
||||
|
||||
this.saveHistory();
|
||||
}
|
||||
|
||||
loadHistory () {
|
||||
this.history = store.get(LS_HISTORY_KEY) || [];
|
||||
}
|
||||
|
||||
saveHistory () {
|
||||
if (this.history.length > MAX_HISTORY_LINES) {
|
||||
this.history = this.history.slice(-1 * MAX_HISTORY_LINES);
|
||||
}
|
||||
|
||||
store.set(LS_HISTORY_KEY, this.history.slice());
|
||||
}
|
||||
}
|
17
js/src/dapps/console/Settings/index.js
Normal file
17
js/src/dapps/console/Settings/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './settings';
|
32
js/src/dapps/console/Settings/settings.css
Normal file
32
js/src/dapps/console/Settings/settings.css
Normal file
@ -0,0 +1,32 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
.option {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 0 0 50%;
|
||||
flex-direction: row;
|
||||
margin: 0.5em 0;
|
||||
}
|
70
js/src/dapps/console/Settings/settings.js
Normal file
70
js/src/dapps/console/Settings/settings.js
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import SettingsStore from './settings.store';
|
||||
|
||||
import styles from './settings.css';
|
||||
|
||||
@observer
|
||||
export default class Settings extends Component {
|
||||
settingsStore = SettingsStore.get();
|
||||
|
||||
render () {
|
||||
const { displayTimestamps, executeOnEnter } = this.settingsStore;
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<div className={ styles.option }>
|
||||
<input
|
||||
checked={ executeOnEnter }
|
||||
id='executeOnEnter'
|
||||
onChange={ this.handleExecuteOnEnterChange }
|
||||
type='checkbox'
|
||||
/>
|
||||
<label htmlFor='executeOnEnter'>
|
||||
Execute on <code>Enter</code>
|
||||
</label>
|
||||
</div>
|
||||
<div className={ styles.option }>
|
||||
<input
|
||||
checked={ displayTimestamps }
|
||||
id='displayTimestamps'
|
||||
onChange={ this.handleDisplayTimestampsChange }
|
||||
type='checkbox'
|
||||
/>
|
||||
<label htmlFor='displayTimestamps'>
|
||||
Show timestamps
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
handleDisplayTimestampsChange = (event) => {
|
||||
const { checked } = event.target;
|
||||
|
||||
this.settingsStore.setDisplayTimestamps(checked);
|
||||
};
|
||||
|
||||
handleExecuteOnEnterChange = (event) => {
|
||||
const { checked } = event.target;
|
||||
|
||||
this.settingsStore.setExecuteOnEnter(checked);
|
||||
};
|
||||
}
|
71
js/src/dapps/console/Settings/settings.store.js
Normal file
71
js/src/dapps/console/Settings/settings.store.js
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, observable } from 'mobx';
|
||||
import store from 'store';
|
||||
|
||||
const LS_SETTINGS_KEY = '_console::settings';
|
||||
|
||||
let instance;
|
||||
|
||||
export default class SettingsStore {
|
||||
@observable displayTimestamps = true;
|
||||
@observable executeOnEnter = true;
|
||||
|
||||
constructor () {
|
||||
this.load();
|
||||
}
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new SettingsStore();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
load () {
|
||||
const settings = store.get(LS_SETTINGS_KEY) || {};
|
||||
const { executeOnEnter, displayTimestamps } = settings;
|
||||
|
||||
if (executeOnEnter !== undefined) {
|
||||
this.setExecuteOnEnter(executeOnEnter);
|
||||
}
|
||||
|
||||
if (displayTimestamps !== undefined) {
|
||||
this.setDisplayTimestamps(displayTimestamps);
|
||||
}
|
||||
}
|
||||
|
||||
save () {
|
||||
const { executeOnEnter, displayTimestamps } = this;
|
||||
const settings = { executeOnEnter, displayTimestamps };
|
||||
|
||||
store.set(LS_SETTINGS_KEY, settings);
|
||||
}
|
||||
|
||||
@action
|
||||
setDisplayTimestamps (value) {
|
||||
this.displayTimestamps = value;
|
||||
this.save();
|
||||
}
|
||||
|
||||
@action
|
||||
setExecuteOnEnter (value) {
|
||||
this.executeOnEnter = value;
|
||||
this.save();
|
||||
}
|
||||
}
|
17
js/src/dapps/console/Snippets/index.js
Normal file
17
js/src/dapps/console/Snippets/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './snippets';
|
122
js/src/dapps/console/Snippets/snippets.css
Normal file
122
js/src/dapps/console/Snippets/snippets.css
Normal file
@ -0,0 +1,122 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-right: 1px solid lightgray;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.add {
|
||||
align-items: center;
|
||||
background-color: #fcfcfc;
|
||||
border-bottom: 1px solid lightgray;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
padding: 0.5em 1em;
|
||||
|
||||
.plus {
|
||||
font-size: 15px;
|
||||
font-weight: bold !important;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.code {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
.console {
|
||||
border-top: 1px solid lightgray;
|
||||
max-height: 200px;
|
||||
flex: 0 0 0;
|
||||
|
||||
> * {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
:global(.CodeMirror) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
padding: 0.5em 0.5em 0.5em 1em;
|
||||
|
||||
&.selected {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgb(230, 236, 255);
|
||||
}
|
||||
|
||||
.pristine {
|
||||
font-size: 20px;
|
||||
margin-right: 3px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
.remove {
|
||||
cursor: default;
|
||||
display: inline-flex;
|
||||
font-size: 14px;
|
||||
margin-left: -0.25em;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.inputContainer {
|
||||
background-color: white;
|
||||
border: solid 1px #d8d8d8;
|
||||
margin-right: 0.5em;
|
||||
padding: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input {
|
||||
border: none;
|
||||
font: 11px Arial;
|
||||
width: 100%;
|
||||
}
|
221
js/src/dapps/console/Snippets/snippets.js
Normal file
221
js/src/dapps/console/Snippets/snippets.js
Normal file
@ -0,0 +1,221 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import keycode from 'keycode';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import CodeMirror from 'react-codemirror';
|
||||
import EventListener from 'react-event-listener';
|
||||
|
||||
import Console from '../Console';
|
||||
import SnippetsStore from './snippets.store';
|
||||
|
||||
import styles from './snippets.css';
|
||||
|
||||
@observer
|
||||
export default class Snippets extends Component {
|
||||
snippetsStore = SnippetsStore.get();
|
||||
|
||||
render () {
|
||||
const { code } = this.snippetsStore;
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<EventListener
|
||||
onKeyDown={ this.handleKeyDown }
|
||||
target='window'
|
||||
/>
|
||||
<div className={ styles.panel }>
|
||||
<div
|
||||
className={ styles.add }
|
||||
onClick={ this.handleAddFile }
|
||||
>
|
||||
<span className={ styles.plus }>+</span>
|
||||
<span>New Snippet</span>
|
||||
</div>
|
||||
<div className={ styles.list }>
|
||||
{ this.renderFiles() }
|
||||
</div>
|
||||
</div>
|
||||
<div className={ styles.code }>
|
||||
<CodeMirror
|
||||
ref={ this.setRef }
|
||||
onChange={ this.handleChange }
|
||||
options={ {
|
||||
autofocus: true,
|
||||
extraKeys: {
|
||||
'Ctrl-Space': 'autocomplete'
|
||||
},
|
||||
keyMap: 'sublime',
|
||||
highlightSelectionMatches: {
|
||||
delay: 0,
|
||||
showToken: false
|
||||
},
|
||||
lineNumbers: true,
|
||||
mode: 'javascript'
|
||||
} }
|
||||
value={ code }
|
||||
/>
|
||||
<div className={ styles.console }>
|
||||
<Console />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderFiles () {
|
||||
const { files } = this.snippetsStore;
|
||||
|
||||
return files
|
||||
.values()
|
||||
.sort((fa, fb) => fa.name.localeCompare(fb.name))
|
||||
.map((file) => this.renderFile(file));
|
||||
}
|
||||
|
||||
renderFile (file) {
|
||||
const { nextName, renaming, selected } = this.snippetsStore;
|
||||
const { id, name } = file;
|
||||
const classes = [ styles.file ];
|
||||
|
||||
if (renaming === id) {
|
||||
return (
|
||||
<div
|
||||
className={ classes.join(' ') }
|
||||
key={ id }
|
||||
>
|
||||
<EventListener
|
||||
onClick={ this.handleSaveName }
|
||||
target='window'
|
||||
/>
|
||||
<div className={ styles.inputContainer }>
|
||||
<input
|
||||
autoFocus
|
||||
className={ styles.input }
|
||||
onClick={ this.stopPropagation }
|
||||
onChange={ this.handleNameChange }
|
||||
onKeyDown={ this.handleRenameKeyDown }
|
||||
type='text'
|
||||
value={ nextName }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const onClick = () => this.handleSelectFile(id);
|
||||
const onDoubleClick = () => this.handleRenameFile(id);
|
||||
const onRemove = (event) => this.handleRemove(id, event);
|
||||
|
||||
if (selected === id) {
|
||||
classes.push(styles.selected);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ classes.join(' ') }
|
||||
key={ id }
|
||||
onClick={ onClick }
|
||||
onDoubleClick={ onDoubleClick }
|
||||
>
|
||||
<span
|
||||
className={ styles.remove }
|
||||
onClick={ onRemove }
|
||||
title={ `Remove ${name}` }
|
||||
>
|
||||
✖
|
||||
</span>
|
||||
<span className={ styles.pristine }>
|
||||
{
|
||||
file.isPristine
|
||||
? null
|
||||
: '*'
|
||||
}
|
||||
</span>
|
||||
<span>
|
||||
{ name }
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
handleAddFile = () => {
|
||||
this.snippetsStore.create();
|
||||
};
|
||||
|
||||
handleSaveName = (event) => {
|
||||
this.snippetsStore.saveName();
|
||||
return event;
|
||||
};
|
||||
|
||||
handleChange = (value) => {
|
||||
this.snippetsStore.edit(value);
|
||||
};
|
||||
|
||||
handleKeyDown = (event) => {
|
||||
const codeName = keycode(event);
|
||||
|
||||
if (codeName === 's' && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
return this.snippetsStore.save();
|
||||
}
|
||||
};
|
||||
|
||||
handleNameChange = (event) => {
|
||||
const { value } = event.target;
|
||||
|
||||
this.snippetsStore.updateName(value);
|
||||
};
|
||||
|
||||
handleRemove = (id, event) => {
|
||||
this.snippetsStore.remove(id);
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
handleRenameFile = (id) => {
|
||||
this.snippetsStore.startRename(id);
|
||||
};
|
||||
|
||||
handleRenameKeyDown = (event) => {
|
||||
const codeName = keycode(event);
|
||||
|
||||
if (codeName === 'enter') {
|
||||
return this.snippetsStore.saveName();
|
||||
}
|
||||
|
||||
if (codeName === 'esc') {
|
||||
return this.snippetsStore.cancelRename();
|
||||
}
|
||||
};
|
||||
|
||||
handleSelectFile = (id) => {
|
||||
this.snippetsStore.select(id);
|
||||
};
|
||||
|
||||
setRef = (node) => {
|
||||
const codeMirror = node
|
||||
? node.getCodeMirror()
|
||||
: null;
|
||||
|
||||
this.snippetsStore.setCodeMirror(codeMirror);
|
||||
};
|
||||
|
||||
stopPropagation = (event) => {
|
||||
event.stopPropagation();
|
||||
};
|
||||
}
|
249
js/src/dapps/console/Snippets/snippets.store.js
Normal file
249
js/src/dapps/console/Snippets/snippets.store.js
Normal file
@ -0,0 +1,249 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import keycode from 'keycode';
|
||||
import { action, computed, map, observable, transaction } from 'mobx';
|
||||
import store from 'store';
|
||||
|
||||
import { evaluate } from '../utils';
|
||||
|
||||
const LS_SNIPPETS_KEY = '_console::snippets';
|
||||
|
||||
let instance;
|
||||
|
||||
export default class SnippetsStore {
|
||||
@observable files = map();
|
||||
@observable nextName = null;
|
||||
@observable renaming = null;
|
||||
@observable selected = null;
|
||||
|
||||
codeMirror = null;
|
||||
|
||||
constructor () {
|
||||
this.load();
|
||||
}
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new SnippetsStore();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@computed
|
||||
get code () {
|
||||
if (!this.selected) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return this.files.get(this.selected).content;
|
||||
}
|
||||
|
||||
@action
|
||||
cancelRename () {
|
||||
if (!this.renaming || !this.nextName) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.renaming = null;
|
||||
this.nextName = null;
|
||||
}
|
||||
|
||||
clearCodeHistory () {
|
||||
if (this.codeMirror) {
|
||||
this.codeMirror.doc.clearHistory();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
create () {
|
||||
const id = this.getNewId();
|
||||
const file = {
|
||||
content: '',
|
||||
isPristine: false,
|
||||
name: `Snippet #${id}`,
|
||||
id
|
||||
};
|
||||
|
||||
transaction(() => {
|
||||
this.files.set(id, file);
|
||||
this.select(id);
|
||||
});
|
||||
}
|
||||
|
||||
edit (value) {
|
||||
if (!this.selected) {
|
||||
this.create();
|
||||
}
|
||||
|
||||
const file = this.files.get(this.selected);
|
||||
|
||||
file.content = value;
|
||||
this.updateFile(file);
|
||||
}
|
||||
|
||||
evaluate () {
|
||||
const code = this.code;
|
||||
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { result, error } = evaluate(code);
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
console.log(result);
|
||||
}
|
||||
}
|
||||
|
||||
getFromStorage () {
|
||||
return store.get(LS_SNIPPETS_KEY) || [];
|
||||
}
|
||||
|
||||
getNewId () {
|
||||
if (this.files.size === 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const ids = this.files.values().map((file) => file.id);
|
||||
|
||||
return Math.max(...ids) + 1;
|
||||
}
|
||||
|
||||
load () {
|
||||
const files = this.getFromStorage();
|
||||
|
||||
transaction(() => {
|
||||
files.forEach((file) => {
|
||||
this.files.set(file.id, file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
remove (id) {
|
||||
transaction(() => {
|
||||
if (id === this.selected) {
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
this.files.delete(id);
|
||||
|
||||
const files = this.getFromStorage()
|
||||
.filter((f) => f.id !== id);
|
||||
|
||||
return store.set(LS_SNIPPETS_KEY, files);
|
||||
});
|
||||
}
|
||||
|
||||
save (_file) {
|
||||
let file;
|
||||
|
||||
if (!_file) {
|
||||
if (!this.selected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file = this.files.get(this.selected);
|
||||
} else {
|
||||
file = _file;
|
||||
}
|
||||
|
||||
file.savedContent = file.content;
|
||||
this.updateFile(file);
|
||||
this.saveToStorage(file);
|
||||
}
|
||||
|
||||
saveName () {
|
||||
if (!this.renaming || !this.nextName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const file = this.files.get(this.renaming);
|
||||
|
||||
file.name = this.nextName;
|
||||
|
||||
this.save(file);
|
||||
this.cancelRename();
|
||||
}
|
||||
|
||||
saveToStorage (file) {
|
||||
const files = this.getFromStorage();
|
||||
const index = files.findIndex((f) => file.id === f.id);
|
||||
|
||||
if (index === -1) {
|
||||
files.push(file);
|
||||
} else {
|
||||
files[index] = file;
|
||||
}
|
||||
|
||||
return store.set(LS_SNIPPETS_KEY, files);
|
||||
}
|
||||
|
||||
@action
|
||||
select (id) {
|
||||
this.selected = id;
|
||||
|
||||
// Wait for the file content to be loaded
|
||||
setTimeout(() => {
|
||||
this.clearCodeHistory();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
setCodeMirror (codeMirror) {
|
||||
this.codeMirror = codeMirror;
|
||||
|
||||
if (!codeMirror) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.codeMirror
|
||||
.on('keydown', (_, event) => {
|
||||
const codeName = keycode(event);
|
||||
|
||||
if (codeName === 'enter' && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return this.evaluate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
startRename (id) {
|
||||
const file = this.files.get(id);
|
||||
|
||||
transaction(() => {
|
||||
this.renaming = id;
|
||||
this.nextName = file.name;
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
updateFile (file) {
|
||||
file.isPristine = (file.content === file.savedContent);
|
||||
|
||||
this.files.set(file.id, file);
|
||||
}
|
||||
|
||||
@action
|
||||
updateName (value) {
|
||||
this.nextName = value;
|
||||
}
|
||||
}
|
17
js/src/dapps/console/Watches/index.js
Normal file
17
js/src/dapps/console/Watches/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './watches';
|
107
js/src/dapps/console/Watches/watches.css
Normal file
107
js/src/dapps/console/Watches/watches.css
Normal file
@ -0,0 +1,107 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
background: #eee;
|
||||
border-top: 1px solid lightgray;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0.25em;
|
||||
font-size: 8pt;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.watch {
|
||||
align-items: flex-end;
|
||||
border-left: 1px solid #ccc;
|
||||
display: flex;
|
||||
padding: 0 1em;
|
||||
|
||||
&.error .result {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.result {
|
||||
color: #00f;
|
||||
font-weight: bold;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.add {
|
||||
color: green;
|
||||
cursor: default;
|
||||
font-size: 14px;
|
||||
font-weight: bold !important;
|
||||
padding: 0 1em;
|
||||
|
||||
&:hover {
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: rgb(230, 236, 255);
|
||||
}
|
||||
}
|
||||
|
||||
.addContainer {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.addForm {
|
||||
bottom: 100%;
|
||||
display: flex;
|
||||
padding: 0.5em 0;
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
|
||||
.inputContainer {
|
||||
border: solid 1px #d8d8d8;
|
||||
margin-right: 0.5em;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.input {
|
||||
border: none;
|
||||
font: 12px Arial;
|
||||
|
||||
&.big {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
color: black;
|
||||
font: 12px Arial;
|
||||
padding: 0 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.remove {
|
||||
cursor: default;
|
||||
display: inline-flex;
|
||||
font-size: 14px;
|
||||
margin-right: 0.75em;
|
||||
|
||||
* {
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
}
|
186
js/src/dapps/console/Watches/watches.js
Normal file
186
js/src/dapps/console/Watches/watches.js
Normal file
@ -0,0 +1,186 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import WatchesStore from './watches.store';
|
||||
|
||||
import styles from './watches.css';
|
||||
|
||||
@observer
|
||||
export default class Watches extends Component {
|
||||
watchesStore = WatchesStore.get();
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
{ this.renderAddWatch() }
|
||||
{ this.renderWatches() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddForm () {
|
||||
const { showAdd } = this.watchesStore;
|
||||
|
||||
if (!showAdd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.addForm }>
|
||||
<div className={ styles.inputContainer }>
|
||||
<input
|
||||
className={ styles.input }
|
||||
onChange={ this.handleAddNameChange }
|
||||
placeholder='Name'
|
||||
type='text'
|
||||
/>
|
||||
</div>
|
||||
<div className={ styles.inputContainer }>
|
||||
<input
|
||||
className={ [ styles.input, styles.big ].join(' ') }
|
||||
onChange={ this.handleAddFunctionChange }
|
||||
placeholder='Function'
|
||||
type='text'
|
||||
/>
|
||||
</div>
|
||||
<div className={ styles.inputContainer }>
|
||||
<input
|
||||
className={ styles.input }
|
||||
onChange={ this.handleAddContextChange }
|
||||
placeholder='Context'
|
||||
type='text'
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className={ styles.button }
|
||||
onClick={ this.handleAddWatch }
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddWatch () {
|
||||
const { showAdd } = this.watchesStore;
|
||||
const classes = [ styles.add ];
|
||||
|
||||
if (showAdd) {
|
||||
classes.push(styles.selected);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.addContainer }>
|
||||
{ this.renderAddForm() }
|
||||
<span
|
||||
className={ classes.join(' ') }
|
||||
onClick={ this.handleToggleAdd }
|
||||
>
|
||||
+
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderWatches () {
|
||||
const { names } = this.watchesStore;
|
||||
|
||||
return names.map((name) => {
|
||||
const { result, error } = this.watchesStore.get(name);
|
||||
const classes = [ styles.watch ];
|
||||
const resultStr = error
|
||||
? error.toString()
|
||||
: this.toString(result);
|
||||
|
||||
const onClick = () => this.handleRemoveWatch(name);
|
||||
|
||||
if (error) {
|
||||
classes.push(styles.error);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ classes.join(' ') }
|
||||
key={ name }
|
||||
>
|
||||
<span
|
||||
className={ styles.remove }
|
||||
onClick={ onClick }
|
||||
title={ `Remove "${name}" watch` }
|
||||
>
|
||||
<span>✖</span>
|
||||
</span>
|
||||
|
||||
<span>{ name }</span>
|
||||
<span className={ styles.result }>{ resultStr.toString() }</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
handleAddFunctionChange = (event) => {
|
||||
const { value } = event.target;
|
||||
|
||||
this.watchesStore.updateAddFunction(value);
|
||||
};
|
||||
|
||||
handleAddContextChange = (event) => {
|
||||
const { value } = event.target;
|
||||
|
||||
this.watchesStore.updateAddContext(value);
|
||||
};
|
||||
|
||||
handleAddNameChange = (event) => {
|
||||
const { value } = event.target;
|
||||
|
||||
this.watchesStore.updateAddName(value);
|
||||
};
|
||||
|
||||
handleAddWatch = () => {
|
||||
this.watchesStore.addWatch();
|
||||
};
|
||||
|
||||
handleRemoveWatch = (name) => {
|
||||
this.watchesStore.remove(name);
|
||||
};
|
||||
|
||||
handleToggleAdd = () => {
|
||||
this.watchesStore.toggleAdd();
|
||||
};
|
||||
|
||||
toString (result) {
|
||||
if (result === undefined) {
|
||||
return 'undefined';
|
||||
}
|
||||
|
||||
if (result === null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (typeof result.toFormat === 'function') {
|
||||
return result.toFormat();
|
||||
}
|
||||
|
||||
if (typeof result.toString === 'function') {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
148
js/src/dapps/console/Watches/watches.store.js
Normal file
148
js/src/dapps/console/Watches/watches.store.js
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, map, observable } from 'mobx';
|
||||
|
||||
import { api } from '../parity';
|
||||
import { evaluate } from '../utils';
|
||||
|
||||
let instance;
|
||||
|
||||
export default class WatchesStore {
|
||||
@observable showAdd = false;
|
||||
@observable watches = map();
|
||||
|
||||
watchesFunctions = {};
|
||||
|
||||
constructor () {
|
||||
api.subscribe('eth_blockNumber', () => {
|
||||
this.refreshWatches();
|
||||
});
|
||||
}
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new WatchesStore();
|
||||
|
||||
window.watch = instance.add.bind(instance);
|
||||
window.unwatch = instance.remove.bind(instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@action
|
||||
add (name, func, context) {
|
||||
if (!func || (typeof func !== 'function' && typeof func.then !== 'function')) {
|
||||
return console.error(Error(`cannot watch ${name} ; not a Function/Promise given`));
|
||||
}
|
||||
|
||||
this.watchesFunctions[name] = { func, context };
|
||||
this.watches.set(name, {});
|
||||
this.refreshWatches();
|
||||
}
|
||||
|
||||
addWatch () {
|
||||
this.toggleAdd();
|
||||
|
||||
const { addContext, addFunction, addName } = this;
|
||||
|
||||
const evaluatedFunction = evaluate(addFunction);
|
||||
const evaluatedContext = addContext
|
||||
? evaluate(addContext)
|
||||
: {};
|
||||
|
||||
this.add(addName, evaluatedFunction.result, evaluatedContext.result);
|
||||
}
|
||||
|
||||
get (name) {
|
||||
return this.watches.get(name);
|
||||
}
|
||||
|
||||
get names () {
|
||||
return this.watches.keys();
|
||||
}
|
||||
|
||||
refreshWatches () {
|
||||
const names = this.watches.keys();
|
||||
const promises = names
|
||||
.map((name) => {
|
||||
const { context, func } = this.watchesFunctions[name];
|
||||
let result;
|
||||
|
||||
try {
|
||||
if (typeof func === 'function') {
|
||||
result = func.apply(context || this);
|
||||
} else {
|
||||
result = func;
|
||||
}
|
||||
|
||||
return Promise.resolve(result);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
})
|
||||
.map((promise, index) => {
|
||||
const name = names[index];
|
||||
|
||||
return promise
|
||||
.then((result) => {
|
||||
this.updateWatch(name, result);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.updateWatch(name, error, true);
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
@action
|
||||
remove (name) {
|
||||
this.watches.delete(name);
|
||||
delete this.watchesFunctions[name];
|
||||
}
|
||||
|
||||
@action
|
||||
toggleAdd () {
|
||||
this.showAdd = !this.showAdd;
|
||||
}
|
||||
|
||||
updateAddContext (value) {
|
||||
this.addContext = value;
|
||||
}
|
||||
|
||||
updateAddFunction (value) {
|
||||
this.addFunction = value;
|
||||
}
|
||||
|
||||
updateAddName (value) {
|
||||
this.addName = value;
|
||||
}
|
||||
|
||||
@action
|
||||
updateWatch (name, result, isError = false) {
|
||||
const next = {};
|
||||
|
||||
if (isError) {
|
||||
next.error = result;
|
||||
} else {
|
||||
next.result = result;
|
||||
}
|
||||
|
||||
this.watches.set(name, { ...next });
|
||||
}
|
||||
}
|
36
js/src/dapps/console/codemirror.css
Normal file
36
js/src/dapps/console/codemirror.css
Normal file
@ -0,0 +1,36 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
:global {
|
||||
.cm-matchhighlight {
|
||||
background-color: rgba(255, 255, 0, 0.4);
|
||||
}
|
||||
|
||||
.CodeMirror-hints {
|
||||
font-family: dejavu sans mono, monospace;
|
||||
font-size: 11px;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-hint {
|
||||
border-radius: 0;
|
||||
cursor: default;
|
||||
padding: 0.25em 0.25em 0.25em 0.35em;
|
||||
}
|
||||
}
|
335
js/src/dapps/console/parity.js
Normal file
335
js/src/dapps/console/parity.js
Normal file
File diff suppressed because one or more lines are too long
36
js/src/dapps/console/utils.js
Normal file
36
js/src/dapps/console/utils.js
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/* eslint-disable no-eval */
|
||||
|
||||
export function evaluate (input) {
|
||||
try {
|
||||
const result = eval.apply(window, [ `${input}` ]);
|
||||
|
||||
return { result };
|
||||
} catch (err) {
|
||||
try {
|
||||
const result = eval.apply(window, [ `(function () {
|
||||
var x = ${input};
|
||||
return x;
|
||||
})()` ]);
|
||||
|
||||
return { result };
|
||||
} catch (error) {
|
||||
return { error };
|
||||
}
|
||||
}
|
||||
}
|
0
js/src/dapps/static/.gitkeep
Normal file
0
js/src/dapps/static/.gitkeep
Normal file
@ -1,221 +0,0 @@
|
||||
#full-screen {
|
||||
}
|
||||
|
||||
textarea:focus, input:focus{
|
||||
outline: none;
|
||||
}
|
||||
|
||||
* { padding: 0; margin: 0; }
|
||||
|
||||
html, body, #full-screen {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#full-screen {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#history-wrap {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#history {
|
||||
flex: 1 1 auto;
|
||||
|
||||
display: flex;
|
||||
min-height: min-content;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.entry, #input {
|
||||
margin: 0;
|
||||
padding: 0.25em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.type {
|
||||
color: #ccc;
|
||||
font-weight: bold;
|
||||
font-family: "Lucida Console", Monaco, monospace;
|
||||
font-size: 11pt;
|
||||
padding: 0 0.5em 0 0.25em;
|
||||
}
|
||||
|
||||
.command .type {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.result .type {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
#input .type {
|
||||
color: #59f;
|
||||
}
|
||||
|
||||
.addwatch {
|
||||
background-color: #cfc;
|
||||
border-top: 1px solid #6e6;
|
||||
}
|
||||
.addwatch .type {
|
||||
color: #040;
|
||||
}
|
||||
.addwatch .text {
|
||||
color: #080;
|
||||
}
|
||||
|
||||
.log.logLevel .type {
|
||||
color: blue;
|
||||
}
|
||||
.log.debugLevel .text {
|
||||
color: blue;
|
||||
}
|
||||
.log.infoLevel .type {
|
||||
color: #00c;
|
||||
}
|
||||
.log.warnLevel {
|
||||
background-color: #fff8dd;
|
||||
border-top: 1px solid #fe8;
|
||||
border-bottom: 1px solid #fe8;
|
||||
}
|
||||
.log.warnLevel .text {
|
||||
color: #440;
|
||||
}
|
||||
.log.warnLevel .type {
|
||||
color: #880;
|
||||
}
|
||||
.log.errorLevel, .error {
|
||||
background-color: #fee;
|
||||
border-top: 1px solid #fbb;
|
||||
border-bottom: 1px solid #fbb;
|
||||
}
|
||||
.log.errorLevel .text, .error .text {
|
||||
color: #c00;
|
||||
}
|
||||
.log.errorLevel .type, .error .type {
|
||||
color: #800;
|
||||
}
|
||||
|
||||
|
||||
span.text {
|
||||
color: black;
|
||||
}
|
||||
.text {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
color: black;
|
||||
font-weight: 1000;
|
||||
font-family: "Lucida Console", Monaco, monospace;
|
||||
font-size: 11pt;
|
||||
|
||||
flex-grow: 1;
|
||||
}
|
||||
div.error {
|
||||
background-color: #fee;
|
||||
border-top: 1px solid #fbb;
|
||||
color: red;
|
||||
}
|
||||
div.command {
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
div#input {
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
#status {
|
||||
background: #eee;
|
||||
padding: 0.25em;
|
||||
font-family: "Lucida Console", Monaco, monospace;
|
||||
font-size: 8pt;
|
||||
color: #888;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
#input {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
#status {
|
||||
display: flex;
|
||||
}
|
||||
.watch {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
.watch:not(:first-child) {
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
.expr {
|
||||
color: #888;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.res {
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.stringType {
|
||||
color: #c00;
|
||||
}
|
||||
.numberType {
|
||||
color: #00f;
|
||||
}
|
||||
.eObject {
|
||||
color: #00f;
|
||||
}
|
||||
.objectType {
|
||||
font-style: italic;
|
||||
}
|
||||
.fieldType {
|
||||
color: #808;
|
||||
}
|
||||
.undefinedType {
|
||||
color: #888;
|
||||
}
|
||||
.functionType {
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#autocomplete-anchor {
|
||||
position: relative;
|
||||
}
|
||||
#autocomplete {
|
||||
position: absolute;
|
||||
left: 1.5em;
|
||||
bottom: 0;
|
||||
background: #f8f8f8;
|
||||
box-shadow: 0 0.125em 0.25em rgba(0, 0, 0, 0.5);
|
||||
max-height: 20em;
|
||||
overflow: scroll;
|
||||
}
|
||||
#autocomplete div {
|
||||
font-size: small;
|
||||
font-family: "Lucida Console", Monaco, monospace;
|
||||
padding: 0.1em 1em 0.1em 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.ac-already {
|
||||
font-weight: bold;
|
||||
}
|
||||
.ac-new {
|
||||
color: #666;
|
||||
}
|
||||
.ac-selected {
|
||||
background-color: #ccddff;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JS Console dapp</title>
|
||||
<link href="console.css" rel='stylesheet' type='text/css'>
|
||||
<script src="/web3.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="full-screen">
|
||||
<div id="eval"></div>
|
||||
<div id="history-wrap"><div id="history"></div></div>
|
||||
<div id="autocomplete-anchor"><div id="autocomplete"></div></div>
|
||||
<div id="input"><span class="type">></span><input type="text" class="text" id="command" list="input-datalist"></div>
|
||||
<div id="status"></div>
|
||||
</div>
|
||||
<script src="console.js"></script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
@ -22,7 +22,7 @@
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 300 !important;
|
||||
font-weight: 300;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
@ -39,7 +39,7 @@
|
||||
}
|
||||
|
||||
:root * {
|
||||
font-weight: 300 !important;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
:root :global(#container) > div {
|
||||
|
@ -7,6 +7,17 @@
|
||||
"version": "1.0.0",
|
||||
"visible": false
|
||||
},
|
||||
{
|
||||
"id": "0xa635a9326814bded464190eddf0bdb90ce92d40ea2359cf553ea80e3c5a4076c",
|
||||
"url": "console",
|
||||
"name": "Parity/Web3 console",
|
||||
"description": "A Javascript development console complete with web3 and parity objects",
|
||||
"version": "0.3",
|
||||
"author": "Gav Wood <gavin@parity.io>",
|
||||
"position": "top-right",
|
||||
"visible": true,
|
||||
"secure": true
|
||||
},
|
||||
{
|
||||
"id": "0xf9f2d620c2e08f83e45555247146c62185e4ab7cf82a4b9002a265a0d020348f",
|
||||
"url": "tokendeploy",
|
||||
@ -84,17 +95,5 @@
|
||||
"visible": true,
|
||||
"skipBuild": true,
|
||||
"skipHistory": true
|
||||
},
|
||||
{
|
||||
"id": "0xa635a9326814bded464190eddf0bdb90ce92d40ea2359cf553ea80e3c5a4076c",
|
||||
"url": "console",
|
||||
"name": "Parity/Web3 console",
|
||||
"description": "A Javascript development console complete with web3 and parity objects",
|
||||
"version": "0.3",
|
||||
"author": "Gav Wood <gavin@parity.io>",
|
||||
"position": "top-right",
|
||||
"visible": true,
|
||||
"secure": true,
|
||||
"skipBuild": true
|
||||
}
|
||||
]
|
||||
|
56
js/yarn.lock
56
js/yarn.lock
@ -1053,7 +1053,7 @@ babel-register@6.23.0, babel-register@^6.23.0:
|
||||
mkdirp "^0.5.1"
|
||||
source-map-support "^0.4.2"
|
||||
|
||||
babel-runtime@6.23.0, babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.20.0, babel-runtime@^6.22.0:
|
||||
babel-runtime@6.23.0, babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.20.0, babel-runtime@^6.22.0, babel-runtime@^6.9.2:
|
||||
version "6.23.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
|
||||
dependencies:
|
||||
@ -1551,7 +1551,7 @@ clap@^1.0.9:
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
|
||||
classnames@2.2.5, classnames@^2.2.0:
|
||||
classnames@2.2.5, classnames@^2.2.0, classnames@^2.2.5:
|
||||
version "2.2.5"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
|
||||
|
||||
@ -1637,6 +1637,10 @@ code-point-at@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||
|
||||
codemirror@^5.18.2:
|
||||
version "5.25.2"
|
||||
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.25.2.tgz#8c77677ca9c9248d757d3a07ed1e89a8404850b7"
|
||||
|
||||
collapse-white-space@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.2.tgz#9c463fb9c6d190d2dcae21a356a01bcae9eeef6d"
|
||||
@ -2118,6 +2122,13 @@ dashdash@^1.12.0:
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
date-difference@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/date-difference/-/date-difference-1.0.0.tgz#d3e6e883c0cad2dc9dbfcbf12e06297b97298974"
|
||||
dependencies:
|
||||
get-stdin "^3.0.2"
|
||||
meow "^3.1.0"
|
||||
|
||||
date-now@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-1.0.1.tgz#bb7d086438debe4182a485fb3df3fbfb99d6153c"
|
||||
@ -3332,6 +3343,10 @@ get-proxy@^1.0.1:
|
||||
dependencies:
|
||||
rc "^1.1.2"
|
||||
|
||||
get-stdin@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-3.0.2.tgz#c1ced24b9039b38ded85bdf161e57713b6dd4abe"
|
||||
|
||||
get-stdin@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
|
||||
@ -4086,6 +4101,10 @@ is-date-object@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
|
||||
|
||||
is-dom@^1.0.5:
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/is-dom/-/is-dom-1.0.9.tgz#483832d52972073de12b9fe3f60320870da8370d"
|
||||
|
||||
is-dotfile@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d"
|
||||
@ -4563,9 +4582,9 @@ keycode@^2.1.1:
|
||||
version "2.1.8"
|
||||
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.8.tgz#94d2b7098215eff0e8f9a8931d5a59076c4532fb"
|
||||
|
||||
keythereum@0.4.3:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/keythereum/-/keythereum-0.4.3.tgz#514acfec3cc4d04b33a21f5d081600719457d2c0"
|
||||
keythereum@0.4.6:
|
||||
version "0.4.6"
|
||||
resolved "https://registry.yarnpkg.com/keythereum/-/keythereum-0.4.6.tgz#0fdec7cf938ae39e5e3061b2f6e16677b766c5da"
|
||||
dependencies:
|
||||
elliptic "6.4.0"
|
||||
ethereumjs-util "5.1.1"
|
||||
@ -4775,6 +4794,10 @@ lodash.create@3.1.1:
|
||||
lodash._basecreate "^3.0.0"
|
||||
lodash._isiterateecall "^3.0.0"
|
||||
|
||||
lodash.debounce@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
|
||||
lodash.defaults@^4.0.1:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
|
||||
@ -5237,11 +5260,11 @@ multipipe@^0.1.2:
|
||||
dependencies:
|
||||
duplexer2 "0.0.2"
|
||||
|
||||
mute-stream@0.0.5, mute-stream@~0.0.4:
|
||||
mute-stream@0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
|
||||
|
||||
mute-stream@0.0.7:
|
||||
mute-stream@0.0.7, mute-stream@~0.0.4:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
|
||||
@ -6524,6 +6547,14 @@ react-addons-test-utils@15.4.2:
|
||||
fbjs "^0.8.4"
|
||||
object-assign "^4.1.0"
|
||||
|
||||
react-codemirror@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-codemirror/-/react-codemirror-0.3.0.tgz#cd6bd6ef458ec1e035cfd8b3fe7b30c8c7883c6c"
|
||||
dependencies:
|
||||
classnames "^2.2.5"
|
||||
codemirror "^5.18.2"
|
||||
lodash.debounce "^4.0.8"
|
||||
|
||||
react-container-dimensions@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-container-dimensions/-/react-container-dimensions-1.2.0.tgz#bfb5e70e10aa82d2ecba49147d14bb4c22cafdaa"
|
||||
@ -6596,6 +6627,13 @@ react-hot-loader@3.0.0-beta.6:
|
||||
redbox-react "^1.2.5"
|
||||
source-map "^0.4.4"
|
||||
|
||||
react-inspector@paritytech/react-inspector:
|
||||
version "1.1.2"
|
||||
resolved "https://codeload.github.com/paritytech/react-inspector/tar.gz/73b5214261a5131821eb9088f58d7e5f31210c23"
|
||||
dependencies:
|
||||
babel-runtime "^6.9.2"
|
||||
is-dom "^1.0.5"
|
||||
|
||||
react-intl-aggregate-webpack-plugin@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-intl-aggregate-webpack-plugin/-/react-intl-aggregate-webpack-plugin-0.0.1.tgz#c29958860d7bcdfa6f460dec79f55ae0220488c6"
|
||||
@ -6958,7 +6996,7 @@ request-capture-har@^1.1.4:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/request-capture-har/-/request-capture-har-1.2.2.tgz#cd692cfb2cc744fd84a3358aac6ee51528cf720d"
|
||||
|
||||
request@2, request@^2.75.0, request@^2.81.0:
|
||||
request@2, request@^2.75.0, request@^2.79.0, request@^2.81.0:
|
||||
version "2.81.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
||||
dependencies:
|
||||
@ -6985,7 +7023,7 @@ request@2, request@^2.75.0, request@^2.81.0:
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.0.0"
|
||||
|
||||
request@2.79.0, request@^2.79.0:
|
||||
request@2.79.0:
|
||||
version "2.79.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user