Merge branch 'master' into ng-accounts-backup
This commit is contained in:
45
js/src/ui/Actionbar/Import/import.css
vendored
Normal file
45
js/src/ui/Actionbar/Import/import.css
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Copyright 2015, 2016 Ethcore (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/>.
|
||||
*/
|
||||
|
||||
.importZone {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
border-width: 2px;
|
||||
border-color: #666;
|
||||
border-style: dashed;
|
||||
border-radius: 10px;
|
||||
|
||||
background-color: rgba(50, 50, 50, 0.2);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2em;
|
||||
|
||||
transition: all 0.5s ease;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
background-color: rgba(50, 50, 50, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-top: 0;
|
||||
color: #ccc;
|
||||
}
|
||||
198
js/src/ui/Actionbar/Import/import.js
Normal file
198
js/src/ui/Actionbar/Import/import.js
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright 2015, 2016 Ethcore (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, { Component, PropTypes } from 'react';
|
||||
import Dropzone from 'react-dropzone';
|
||||
import FileUploadIcon from 'material-ui/svg-icons/file/file-upload';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||
|
||||
import { Modal, Button } from '../../';
|
||||
|
||||
import styles from './import.css';
|
||||
|
||||
const initialState = {
|
||||
step: 0,
|
||||
show: false,
|
||||
validate: false,
|
||||
validationBody: null,
|
||||
content: ''
|
||||
};
|
||||
|
||||
export default class ActionbarImport extends Component {
|
||||
|
||||
static propTypes = {
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
renderValidation: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
title: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
title: 'Import from a file'
|
||||
};
|
||||
|
||||
state = Object.assign({}, initialState);
|
||||
|
||||
render () {
|
||||
const { className } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
className={ className }
|
||||
icon={ <FileUploadIcon /> }
|
||||
label='import'
|
||||
onClick={ this.onOpenModal }
|
||||
/>
|
||||
{ this.renderModal() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderModal () {
|
||||
const { title, renderValidation } = this.props;
|
||||
const { show, step } = this.state;
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasSteps = typeof renderValidation === 'function';
|
||||
|
||||
const steps = hasSteps ? [ 'select a file', 'validate' ] : null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
actions={ this.renderActions() }
|
||||
title={ title }
|
||||
steps={ steps }
|
||||
current={ step }
|
||||
visible
|
||||
>
|
||||
{ this.renderBody() }
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderActions () {
|
||||
const { validate } = this.state;
|
||||
|
||||
const cancelBtn = (
|
||||
<Button
|
||||
key='cancel'
|
||||
label='Cancel'
|
||||
icon={ <ContentClear /> }
|
||||
onClick={ this.onCloseModal }
|
||||
/>
|
||||
);
|
||||
|
||||
if (validate) {
|
||||
const confirmBtn = (
|
||||
<Button
|
||||
key='confirm'
|
||||
label='Confirm'
|
||||
icon={ <ActionDoneAll /> }
|
||||
onClick={ this.onConfirm }
|
||||
/>
|
||||
);
|
||||
|
||||
return [ cancelBtn, confirmBtn ];
|
||||
}
|
||||
|
||||
return [ cancelBtn ];
|
||||
}
|
||||
|
||||
renderBody () {
|
||||
const { validate } = this.state;
|
||||
|
||||
if (validate) {
|
||||
return this.renderValidation();
|
||||
}
|
||||
|
||||
return this.renderFileSelect();
|
||||
}
|
||||
|
||||
renderFileSelect () {
|
||||
return (
|
||||
<div>
|
||||
<Dropzone
|
||||
onDrop={ this.onDrop }
|
||||
multiple={ false }
|
||||
className={ styles.importZone }
|
||||
>
|
||||
<div>Drop a file here, or click to select a file to upload.</div>
|
||||
</Dropzone>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderValidation () {
|
||||
const { validationBody } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p className={ styles.desc }>
|
||||
Confirm that this is what was intended to import.
|
||||
</p>
|
||||
<div>
|
||||
{ validationBody }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onDrop = (files) => {
|
||||
const { renderValidation } = this.props;
|
||||
|
||||
const file = files[0];
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
const content = e.target.result;
|
||||
|
||||
if (typeof renderValidation !== 'function') {
|
||||
this.props.onConfirm(content);
|
||||
return this.onCloseModal();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
step: 1,
|
||||
validate: true,
|
||||
validationBody: renderValidation(content),
|
||||
content
|
||||
});
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
onConfirm = () => {
|
||||
const { content } = this.state;
|
||||
|
||||
this.props.onConfirm(content);
|
||||
return this.onCloseModal();
|
||||
}
|
||||
|
||||
onOpenModal = () => {
|
||||
this.setState({ show: true });
|
||||
}
|
||||
|
||||
onCloseModal = () => {
|
||||
this.setState(initialState);
|
||||
}
|
||||
|
||||
}
|
||||
17
js/src/ui/Actionbar/Import/index.js
Normal file
17
js/src/ui/Actionbar/Import/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 './import';
|
||||
@@ -41,11 +41,3 @@
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.chip > svg {
|
||||
width: 1.2rem !important;
|
||||
height: 1.2rem !important;
|
||||
margin: initial !important;
|
||||
margin-right: 4px !important;
|
||||
padding: 4px 0 !important;
|
||||
}
|
||||
|
||||
@@ -15,14 +15,9 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Chip } from 'material-ui';
|
||||
import { blue300 } from 'material-ui/styles/colors';
|
||||
// import ChipInput from 'material-ui-chip-input';
|
||||
import ChipInput from 'material-ui-chip-input/src/ChipInput';
|
||||
import ActionSearch from 'material-ui/svg-icons/action/search';
|
||||
import { uniq } from 'lodash';
|
||||
|
||||
import { Button } from '../../';
|
||||
import { Button, InputChip } from '../../';
|
||||
|
||||
import styles from './search.css';
|
||||
|
||||
@@ -74,28 +69,16 @@ export default class ActionbarSearch extends Component {
|
||||
className={ styles.searchcontainer }
|
||||
key='searchAccount'>
|
||||
<div className={ inputContainerClasses.join(' ') }>
|
||||
<ChipInput
|
||||
clearOnBlur={ false }
|
||||
<InputChip
|
||||
className={ styles.input }
|
||||
chipRenderer={ this.chipRenderer }
|
||||
hintText='Enter search input...'
|
||||
ref='searchInput'
|
||||
value={ tokens }
|
||||
hint='Enter search input...'
|
||||
tokens={ tokens }
|
||||
|
||||
onBlur={ this.handleSearchBlur }
|
||||
onRequestAdd={ this.handleTokenAdd }
|
||||
onRequestDelete={ this.handleTokenDelete }
|
||||
onUpdateInput={ this.handleInputChange }
|
||||
hintStyle={ {
|
||||
bottom: 16,
|
||||
left: 2,
|
||||
transition: 'none'
|
||||
} }
|
||||
inputStyle={ {
|
||||
marginBottom: 18
|
||||
} }
|
||||
textFieldStyle={ {
|
||||
height: 42
|
||||
} }
|
||||
onInputChange={ this.handleInputChange }
|
||||
onTokensChange={ this.handleTokensChange }
|
||||
|
||||
addOnBlur
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -108,67 +91,13 @@ export default class ActionbarSearch extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
chipRenderer = (state, key) => {
|
||||
const { value, isFocused, isDisabled, handleClick, handleRequestDelete } = state;
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={ key }
|
||||
className={ styles.chip }
|
||||
style={ {
|
||||
margin: '8px 8px 0 0',
|
||||
float: 'left',
|
||||
pointerEvents: isDisabled ? 'none' : undefined,
|
||||
alignItems: 'center'
|
||||
} }
|
||||
labelStyle={ {
|
||||
paddingRight: 6,
|
||||
fontSize: '0.9rem',
|
||||
lineHeight: 'initial'
|
||||
} }
|
||||
backgroundColor={ isFocused ? blue300 : 'rgba(0, 0, 0, 0.73)' }
|
||||
onTouchTap={ handleClick }
|
||||
onRequestDelete={ handleRequestDelete }
|
||||
>
|
||||
{ value }
|
||||
</Chip>
|
||||
);
|
||||
handleTokensChange = (tokens) => {
|
||||
this.handleSearchChange(tokens);
|
||||
}
|
||||
|
||||
handleTokenAdd = (value) => {
|
||||
const { tokens } = this.props;
|
||||
|
||||
const newSearchTokens = uniq([].concat(tokens, value));
|
||||
|
||||
this.handleSearchChange(newSearchTokens);
|
||||
}
|
||||
|
||||
handleTokenDelete = (value) => {
|
||||
const { tokens } = this.props;
|
||||
|
||||
const newSearchTokens = []
|
||||
.concat(tokens)
|
||||
.filter(v => v !== value);
|
||||
|
||||
this.handleSearchChange(newSearchTokens);
|
||||
this.refs.searchInput.focus();
|
||||
}
|
||||
|
||||
handleInputChange = (value) => {
|
||||
const splitTokens = value.split(/[\s,;]/);
|
||||
|
||||
const inputValue = (splitTokens.length <= 1)
|
||||
? value
|
||||
: splitTokens.slice(-1)[0].trim();
|
||||
|
||||
this.refs.searchInput.setState({ inputValue });
|
||||
handleInputChange = (inputValue) => {
|
||||
this.setState({ inputValue }, () => {
|
||||
if (splitTokens.length > 1) {
|
||||
const tokensToAdd = splitTokens.slice(0, -1);
|
||||
tokensToAdd.forEach(token => this.handleTokenAdd(token));
|
||||
} else {
|
||||
this.handleSearchChange();
|
||||
}
|
||||
this.handleSearchChange();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -177,12 +106,10 @@ export default class ActionbarSearch extends Component {
|
||||
const { inputValue } = this.state;
|
||||
|
||||
const newSearchTokens = []
|
||||
.concat(searchTokens || tokens)
|
||||
.filter(v => v.length > 0);
|
||||
.concat(searchTokens || tokens);
|
||||
|
||||
const newSearchValues = []
|
||||
.concat(searchTokens || tokens, inputValue)
|
||||
.filter(v => v.length > 0);
|
||||
.concat(searchTokens || tokens, inputValue);
|
||||
|
||||
onChange(newSearchTokens, newSearchValues);
|
||||
}
|
||||
@@ -209,19 +136,15 @@ export default class ActionbarSearch extends Component {
|
||||
}
|
||||
|
||||
handleOpenSearch = (showSearch, force) => {
|
||||
if (this.state.stateChanging && !force) return false;
|
||||
if (this.state.stateChanging && !force) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
showSearch: showSearch,
|
||||
stateChanging: true
|
||||
});
|
||||
|
||||
if (showSearch) {
|
||||
this.refs.searchInput.focus();
|
||||
} else {
|
||||
this.refs.searchInput.getInputNode().blur();
|
||||
}
|
||||
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
this.setState({ stateChanging: false });
|
||||
}, 450);
|
||||
|
||||
@@ -27,14 +27,23 @@ import styles from './sort.css';
|
||||
export default class ActionbarSort extends Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
order: PropTypes.string
|
||||
order: PropTypes.string,
|
||||
showDefault: PropTypes.bool,
|
||||
metas: PropTypes.array
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
metas: [],
|
||||
showDefault: true
|
||||
}
|
||||
|
||||
state = {
|
||||
menuOpen: false
|
||||
}
|
||||
|
||||
render () {
|
||||
const { showDefault } = this.props;
|
||||
|
||||
return (
|
||||
<IconMenu
|
||||
iconButtonElement={
|
||||
@@ -50,14 +59,56 @@ export default class ActionbarSort extends Component {
|
||||
onItemTouchTap={ this.handleSortChange }
|
||||
targetOrigin={ { horizontal: 'right', vertical: 'top' } }
|
||||
anchorOrigin={ { horizontal: 'right', vertical: 'top' } }
|
||||
>
|
||||
<MenuItem value='' primaryText='Default' />
|
||||
<MenuItem value='tags' primaryText='Sort by tags' />
|
||||
<MenuItem value='name' primaryText='Sort by name' />
|
||||
touchTapCloseDelay={ 0 }
|
||||
>
|
||||
{
|
||||
showDefault
|
||||
? this.renderMenuItem('', 'Default')
|
||||
: null
|
||||
}
|
||||
{ this.renderMenuItem('tags', 'Sort by tags') }
|
||||
{ this.renderMenuItem('name', 'Sort by name') }
|
||||
{ this.renderMenuItem('eth', 'Sort by ETH') }
|
||||
|
||||
{ this.renderSortByMetas() }
|
||||
</IconMenu>
|
||||
);
|
||||
}
|
||||
|
||||
renderSortByMetas () {
|
||||
const { metas } = this.props;
|
||||
|
||||
return metas
|
||||
.map((meta, index) => {
|
||||
return this
|
||||
.renderMenuItem(meta.key, `Sort by ${meta.label}`, index);
|
||||
});
|
||||
}
|
||||
|
||||
renderMenuItem (value, label, key = null) {
|
||||
const { order } = this.props;
|
||||
|
||||
const props = {};
|
||||
|
||||
if (key !== null) {
|
||||
props.key = key;
|
||||
}
|
||||
|
||||
const checked = order === value;
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
checked={ checked }
|
||||
value={ value }
|
||||
primaryText={ label }
|
||||
innerDivStyle={ {
|
||||
paddingLeft: checked ? 50 : 16
|
||||
} }
|
||||
{ ...props }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
handleSortChange = (event, child) => {
|
||||
const order = child.props.value;
|
||||
this.props.onChange(order);
|
||||
|
||||
@@ -32,19 +32,25 @@
|
||||
border-radius: 16px;
|
||||
margin: 0.75em 0.5em 0 0;
|
||||
max-height: 24px;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.balance img {
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
margin: -4px 1em 0 0;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.balance div {
|
||||
display: inline-block;
|
||||
/*font-family: 'Roboto Mono', monospace;*/
|
||||
line-height: 24px;
|
||||
margin: 0 1em 0 0;
|
||||
vertical-align: top;
|
||||
.balanceValue {
|
||||
margin: 0 0.5em 0 0;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.balanceTag {
|
||||
font-size: 0.85em;
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
|
||||
@@ -44,10 +44,31 @@ class Balance extends Component {
|
||||
.filter((balance) => new BigNumber(balance.value).gt(0))
|
||||
.map((balance) => {
|
||||
const token = balance.token;
|
||||
const value = token.format
|
||||
? new BigNumber(balance.value).div(new BigNumber(token.format)).toFormat(3)
|
||||
: api.util.fromWei(balance.value).toFormat(3);
|
||||
const imagesrc = token.image || images[token.address] || unknownImage;
|
||||
|
||||
let value;
|
||||
if (token.format) {
|
||||
const bnf = new BigNumber(token.format);
|
||||
|
||||
let decimals = 0;
|
||||
if (bnf.gte(1000)) {
|
||||
decimals = 3;
|
||||
} else if (bnf.gte(100)) {
|
||||
decimals = 2;
|
||||
} else if (bnf.gte(10)) {
|
||||
decimals = 1;
|
||||
}
|
||||
|
||||
value = new BigNumber(balance.value).div(bnf).toFormat(decimals);
|
||||
} else {
|
||||
value = api.util.fromWei(balance.value).toFormat(3);
|
||||
}
|
||||
|
||||
let imagesrc = token.image;
|
||||
if (!imagesrc) {
|
||||
imagesrc = images[token.address]
|
||||
? `${api.dappsUrl}${images[token.address]}`
|
||||
: unknownImage;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -56,7 +77,10 @@ class Balance extends Component {
|
||||
<img
|
||||
src={ imagesrc }
|
||||
alt={ token.name } />
|
||||
<div>{ value }<small> { token.tag }</small></div>
|
||||
<div className={ styles.balanceValue }>
|
||||
<span title={ value }> { value } </span>
|
||||
</div>
|
||||
<div className={ styles.balanceTag }> { token.tag } </div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
22
js/src/ui/BlockStatus/blockStatus.css
Normal file
22
js/src/ui/BlockStatus/blockStatus.css
Normal file
@@ -0,0 +1,22 @@
|
||||
/* Copyright 2015, 2016 Ethcore (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/>.
|
||||
*/
|
||||
|
||||
.blockNumber {
|
||||
}
|
||||
|
||||
.syncStatus {
|
||||
}
|
||||
90
js/src/ui/BlockStatus/blockStatus.js
Normal file
90
js/src/ui/BlockStatus/blockStatus.js
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2015, 2016 Ethcore (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, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import styles from './blockStatus.css';
|
||||
|
||||
class BlockStatus extends Component {
|
||||
static propTypes = {
|
||||
blockNumber: PropTypes.object,
|
||||
syncing: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.object
|
||||
])
|
||||
}
|
||||
|
||||
render () {
|
||||
const { blockNumber, syncing } = this.props;
|
||||
|
||||
if (!blockNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!syncing) {
|
||||
return (
|
||||
<div className={ styles.blockNumber }>
|
||||
{ blockNumber.toFormat() } best block
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!syncing.warpChunksAmount.eq(syncing.warpChunksProcessed)) {
|
||||
return (
|
||||
<div className={ styles.syncStatus }>
|
||||
{ syncing.warpChunksProcessed.mul(100).div(syncing.warpChunksAmount).toFormat(2) }% warp restore
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let warpStatus = null;
|
||||
|
||||
if (syncing.blockGap) {
|
||||
const [first, last] = syncing.blockGap;
|
||||
|
||||
warpStatus = (
|
||||
<span>, { first.mul(100).div(last).toFormat(2) }% historic</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.syncStatus }>
|
||||
<span>{ syncing.currentBlock.toFormat() }/{ syncing.highestBlock.toFormat() } syncing</span>
|
||||
{ warpStatus }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { blockNumber, syncing } = state.nodeStatus;
|
||||
|
||||
return {
|
||||
blockNumber,
|
||||
syncing
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(BlockStatus);
|
||||
17
js/src/ui/BlockStatus/index.js
Normal file
17
js/src/ui/BlockStatus/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 './blockStatus';
|
||||
@@ -19,6 +19,7 @@ import { FlatButton } from 'material-ui';
|
||||
|
||||
export default class Button extends Component {
|
||||
static propTypes = {
|
||||
backgroundColor: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
icon: PropTypes.node,
|
||||
@@ -26,19 +27,25 @@ export default class Button extends Component {
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.object
|
||||
]),
|
||||
onClick: PropTypes.func
|
||||
onClick: PropTypes.func,
|
||||
primary: PropTypes.bool
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
primary: true
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, disabled, icon, label, onClick } = this.props;
|
||||
const { className, backgroundColor, disabled, icon, label, primary, onClick } = this.props;
|
||||
|
||||
return (
|
||||
<FlatButton
|
||||
className={ className }
|
||||
backgroundColor={ backgroundColor }
|
||||
disabled={ disabled }
|
||||
icon={ icon }
|
||||
label={ label }
|
||||
primary
|
||||
primary={ primary }
|
||||
onTouchTap={ onClick } />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,20 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.byline {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
line-height: 1.2em;
|
||||
max-height: 2.4em;
|
||||
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
color: #aaa;
|
||||
|
||||
* {
|
||||
color: #aaa !important;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
|
||||
@@ -24,7 +24,9 @@ export default class Title extends Component {
|
||||
title: PropTypes.oneOfType([
|
||||
PropTypes.string, PropTypes.node
|
||||
]),
|
||||
byline: PropTypes.string
|
||||
byline: PropTypes.oneOfType([
|
||||
PropTypes.string, PropTypes.node
|
||||
])
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -32,15 +34,23 @@ export default class Title extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className } = this.props;
|
||||
const { className, title, byline } = this.props;
|
||||
|
||||
const byLine = typeof byline === 'string'
|
||||
? (
|
||||
<span title={ byline }>
|
||||
{ byline }
|
||||
</span>
|
||||
)
|
||||
: byline;
|
||||
|
||||
return (
|
||||
<div className={ className }>
|
||||
<h3 className={ styles.title }>
|
||||
{ this.props.title }
|
||||
{ title }
|
||||
</h3>
|
||||
<div className={ styles.byline }>
|
||||
{ this.props.byline }
|
||||
{ byLine }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
.container {
|
||||
flex: 1;
|
||||
padding: 0em;
|
||||
background: rgba(0, 0, 0, 0.8) !important;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.compact,
|
||||
|
||||
24
js/src/ui/CopyToClipboard/copyToClipboard.css
Normal file
24
js/src/ui/CopyToClipboard/copyToClipboard.css
Normal file
@@ -0,0 +1,24 @@
|
||||
/* Copyright 2015, 2016 Ethcore (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/>.
|
||||
*/
|
||||
|
||||
.wrapper {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.data {
|
||||
font-family: monospace;
|
||||
}
|
||||
93
js/src/ui/CopyToClipboard/copyToClipboard.js
Normal file
93
js/src/ui/CopyToClipboard/copyToClipboard.js
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2015, 2016 Ethcore (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, { Component, PropTypes } from 'react';
|
||||
import { IconButton } from 'material-ui';
|
||||
import Snackbar from 'material-ui/Snackbar';
|
||||
import Clipboard from 'react-copy-to-clipboard';
|
||||
import CopyIcon from 'material-ui/svg-icons/content/content-copy';
|
||||
import Theme from '../Theme';
|
||||
import { darkBlack } from 'material-ui/styles/colors';
|
||||
const { textColor, disabledTextColor } = Theme.flatButton;
|
||||
|
||||
import styles from './copyToClipboard.css';
|
||||
|
||||
export default class CopyToClipboard extends Component {
|
||||
static propTypes = {
|
||||
data: PropTypes.string.isRequired,
|
||||
onCopy: PropTypes.func,
|
||||
size: PropTypes.number, // in px
|
||||
cooldown: PropTypes.number // in ms
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
onCopy: () => {},
|
||||
size: 16,
|
||||
cooldown: 1000
|
||||
};
|
||||
|
||||
state = {
|
||||
copied: false,
|
||||
timeout: null
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
const { timeoutId } = this.state;
|
||||
if (timeoutId) {
|
||||
window.clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { data, size } = this.props;
|
||||
const { copied } = this.state;
|
||||
|
||||
return (
|
||||
<Clipboard onCopy={ this.onCopy } text={ data }>
|
||||
<div className={ styles.wrapper }>
|
||||
<Snackbar
|
||||
open={ copied }
|
||||
message={
|
||||
<div>copied <code className={ styles.data }>{ data }</code> to clipboard</div>
|
||||
}
|
||||
autoHideDuration={ 2000 }
|
||||
bodyStyle={ { backgroundColor: darkBlack } }
|
||||
/>
|
||||
<IconButton
|
||||
disableTouchRipple
|
||||
style={ { width: size, height: size, padding: '0' } }
|
||||
iconStyle={ { width: size, height: size } }
|
||||
>
|
||||
<CopyIcon color={ copied ? disabledTextColor : textColor } />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Clipboard>
|
||||
);
|
||||
}
|
||||
|
||||
onCopy = () => {
|
||||
const { cooldown, onCopy } = this.props;
|
||||
|
||||
this.setState({
|
||||
copied: true,
|
||||
timeout: setTimeout(() => {
|
||||
this.setState({ copied: false, timeout: null });
|
||||
}, cooldown)
|
||||
});
|
||||
onCopy();
|
||||
}
|
||||
}
|
||||
17
js/src/ui/CopyToClipboard/index.js
Normal file
17
js/src/ui/CopyToClipboard/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 './copyToClipboard';
|
||||
103
js/src/ui/Editor/editor.js
Normal file
103
js/src/ui/Editor/editor.js
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2015, 2016 Ethcore (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, { PropTypes, Component } from 'react';
|
||||
|
||||
import 'brace';
|
||||
import AceEditor from 'react-ace';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import 'brace/theme/solarized_dark';
|
||||
import 'brace/mode/json';
|
||||
import './mode-solidity';
|
||||
|
||||
export default class Editor extends Component {
|
||||
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
mode: PropTypes.string,
|
||||
maxLines: PropTypes.number,
|
||||
annotations: PropTypes.array,
|
||||
onExecute: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
readOnly: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
value: '',
|
||||
mode: 'javascript',
|
||||
annotations: [],
|
||||
onExecute: noop,
|
||||
onChange: noop,
|
||||
readOnly: false
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
this.name = `PARITY_EDITOR_${Math.round(Math.random() * 99999)}`;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, annotations, value, readOnly, mode, maxLines } = this.props;
|
||||
const commands = [
|
||||
{
|
||||
name: 'execut',
|
||||
bindKey: { win: 'Ctrl-Enter', mac: 'Command-Enter' },
|
||||
exec: this.handleExecute
|
||||
}
|
||||
];
|
||||
|
||||
const max = (maxLines !== undefined)
|
||||
? maxLines
|
||||
: (readOnly ? value.split('\n').length + 1 : null);
|
||||
|
||||
return (
|
||||
<AceEditor
|
||||
mode={ mode }
|
||||
theme='solarized_dark'
|
||||
width='100%'
|
||||
ref='brace'
|
||||
style={ { flex: 1 } }
|
||||
onChange={ this.handleOnChange }
|
||||
name={ this.name }
|
||||
editorProps={ { $blockScrolling: Infinity } }
|
||||
setOptions={ {
|
||||
useWorker: false,
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.9em'
|
||||
} }
|
||||
maxLines={ max }
|
||||
enableBasicAutocompletion={ !readOnly }
|
||||
showPrintMargin={ false }
|
||||
annotations={ annotations }
|
||||
value={ value }
|
||||
commands={ commands }
|
||||
readOnly={ readOnly }
|
||||
className={ className }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
handleExecute = () => {
|
||||
this.props.onExecute();
|
||||
}
|
||||
|
||||
handleOnChange = (value) => {
|
||||
this.props.onChange(value);
|
||||
}
|
||||
|
||||
}
|
||||
17
js/src/ui/Editor/index.js
Normal file
17
js/src/ui/Editor/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 './editor';
|
||||
994
js/src/ui/Editor/mode-solidity.js
Normal file
994
js/src/ui/Editor/mode-solidity.js
Normal file
@@ -0,0 +1,994 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
/**
|
||||
* This file has been taken from `ethereum/browser-solidity`
|
||||
*
|
||||
* @see: https://raw.githubusercontent.com/ethereum/browser-solidity/master/src/mode-solidity.js
|
||||
*/
|
||||
/* eslint-disable */
|
||||
var ace = window.ace;
|
||||
|
||||
ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(acequire, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = acequire("../lib/oop");
|
||||
var TextHighlightRules = acequire("./text_highlight_rules").TextHighlightRules;
|
||||
|
||||
var DocCommentHighlightRules = function() {
|
||||
this.$rules = {
|
||||
"start" : [ {
|
||||
token : "comment.doc.tag",
|
||||
regex : "@[\\w\\d_]+" // TODO: fix email addresses
|
||||
},
|
||||
DocCommentHighlightRules.getTagRule(),
|
||||
{
|
||||
defaultToken : "comment.doc",
|
||||
caseInsensitive: true
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
oop.inherits(DocCommentHighlightRules, TextHighlightRules);
|
||||
|
||||
DocCommentHighlightRules.getTagRule = function(start) {
|
||||
return {
|
||||
token : "comment.doc.tag.storage.type",
|
||||
regex : "\\b(?:TODO|FIXME|XXX|HACK)\\b"
|
||||
};
|
||||
};
|
||||
|
||||
DocCommentHighlightRules.getStartRule = function(start) {
|
||||
return {
|
||||
token : "comment.doc", // doc comment
|
||||
regex : "\\/\\*(?=\\*)",
|
||||
next : start
|
||||
};
|
||||
};
|
||||
|
||||
DocCommentHighlightRules.getEndRule = function (start) {
|
||||
return {
|
||||
token : "comment.doc", // closing comment
|
||||
regex : "\\*\\/",
|
||||
next : start
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
exports.DocCommentHighlightRules = DocCommentHighlightRules;
|
||||
|
||||
});
|
||||
|
||||
ace.define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"], function(acequire, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = acequire("../lib/oop");
|
||||
var DocCommentHighlightRules = acequire("./doc_comment_highlight_rules").DocCommentHighlightRules;
|
||||
var TextHighlightRules = acequire("./text_highlight_rules").TextHighlightRules;
|
||||
|
||||
var JavaScriptHighlightRules = function(options) {
|
||||
var intTypes = 'bytes|int|uint';
|
||||
for (var width = 8; width <= 256; width += 8)
|
||||
intTypes += '|bytes' + (width / 8) + '|uint' + width + '|int' + width;
|
||||
var keywordMapper = this.createKeywordMapper({
|
||||
"variable.language":
|
||||
"this|bool|string|byte|bytes|bytes0|address|" + intTypes,
|
||||
"keyword":
|
||||
"contract|library|constant|event|modifier|" +
|
||||
"struct|mapping|enum|break|continue|delete|else|for|function|" +
|
||||
"if|new|return|returns|var|while|using|" +
|
||||
"private|public|external|internal|storage|memory",
|
||||
"storage.type":
|
||||
"constant|var|function",
|
||||
"constant.language.boolean": "true|false"
|
||||
}, "identifier");
|
||||
var kwBeforeRe = "case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void";
|
||||
var identifierRe = "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b";
|
||||
|
||||
var escapedRe = "\\\\(?:x[0-9a-fA-F]{2}|" + // hex
|
||||
"u[0-9a-fA-F]{4}|" + // unicode
|
||||
"[0-2][0-7]{0,2}|" + // oct
|
||||
"3[0-6][0-7]?|" + // oct
|
||||
"37[0-7]?|" + // oct
|
||||
"[4-7][0-7]?|" + //oct
|
||||
".)";
|
||||
|
||||
this.$rules = {
|
||||
"no_regex" : [
|
||||
{
|
||||
token : "comment",
|
||||
regex : "\\/\\/",
|
||||
next : "line_comment"
|
||||
},
|
||||
DocCommentHighlightRules.getStartRule("doc-start"),
|
||||
{
|
||||
token : "comment", // multi line comment
|
||||
regex : /\/\*/,
|
||||
next : "comment"
|
||||
}, {
|
||||
token : "string",
|
||||
regex : "'(?=.)",
|
||||
next : "qstring"
|
||||
}, {
|
||||
token : "string",
|
||||
regex : '"(?=.)',
|
||||
next : "qqstring"
|
||||
}, {
|
||||
token : "constant.numeric", // hex
|
||||
regex : /0[xX][0-9a-fA-F]+\b/
|
||||
}, {
|
||||
token : "constant.numeric", // float
|
||||
regex : /[+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/
|
||||
}, {
|
||||
token : [
|
||||
"storage.type", "punctuation.operator", "support.function",
|
||||
"punctuation.operator", "entity.name.function", "text","keyword.operator"
|
||||
],
|
||||
regex : "(" + identifierRe + ")(\\.)(prototype)(\\.)(" + identifierRe +")(\\s*)(=)",
|
||||
next: "function_arguments"
|
||||
}, {
|
||||
token : [
|
||||
"storage.type", "punctuation.operator", "entity.name.function", "text",
|
||||
"keyword.operator", "text", "storage.type", "text", "paren.lparen"
|
||||
],
|
||||
regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",
|
||||
next: "function_arguments"
|
||||
}, {
|
||||
token : [
|
||||
"entity.name.function", "text", "keyword.operator", "text", "storage.type",
|
||||
"text", "paren.lparen"
|
||||
],
|
||||
regex : "(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",
|
||||
next: "function_arguments"
|
||||
}, {
|
||||
token : [
|
||||
"storage.type", "punctuation.operator", "entity.name.function", "text",
|
||||
"keyword.operator", "text",
|
||||
"storage.type", "text", "entity.name.function", "text", "paren.lparen"
|
||||
],
|
||||
regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",
|
||||
next: "function_arguments"
|
||||
}, {
|
||||
token : [
|
||||
"storage.type", "text", "entity.name.function", "text", "paren.lparen"
|
||||
],
|
||||
regex : "(function)(\\s+)(" + identifierRe + ")(\\s*)(\\()",
|
||||
next: "function_arguments"
|
||||
}, {
|
||||
token : [
|
||||
"entity.name.function", "text", "punctuation.operator",
|
||||
"text", "storage.type", "text", "paren.lparen"
|
||||
],
|
||||
regex : "(" + identifierRe + ")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",
|
||||
next: "function_arguments"
|
||||
}, {
|
||||
token : [
|
||||
"text", "text", "storage.type", "text", "paren.lparen"
|
||||
],
|
||||
regex : "(:)(\\s*)(function)(\\s*)(\\()",
|
||||
next: "function_arguments"
|
||||
}, {
|
||||
token : "keyword",
|
||||
regex : "(?:" + kwBeforeRe + ")\\b",
|
||||
next : "start"
|
||||
}, {
|
||||
token : ["punctuation.operator", "support.function"],
|
||||
regex : /(\.)(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/
|
||||
}, {
|
||||
token : ["punctuation.operator", "support.function.dom"],
|
||||
regex : /(\.)(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/
|
||||
}, {
|
||||
token : ["punctuation.operator", "support.constant"],
|
||||
regex : /(\.)(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/
|
||||
}, {
|
||||
token : ["support.constant"],
|
||||
regex : /that\b/
|
||||
}, {
|
||||
token : ["storage.type", "punctuation.operator", "support.function.firebug"],
|
||||
regex : /(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/
|
||||
}, {
|
||||
token : keywordMapper,
|
||||
regex : identifierRe
|
||||
}, {
|
||||
token : "keyword.operator",
|
||||
regex : /--|\*\*|\+\+|===|==|=|!=|!==|=>|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,
|
||||
next : "start"
|
||||
}, {
|
||||
token : "punctuation.operator",
|
||||
regex : /[?:,;.]/,
|
||||
next : "start"
|
||||
}, {
|
||||
token : "paren.lparen",
|
||||
regex : /[\[({]/,
|
||||
next : "start"
|
||||
}, {
|
||||
token : "paren.rparen",
|
||||
regex : /[\])}]/
|
||||
}, {
|
||||
token: "comment",
|
||||
regex: /^#!.*$/
|
||||
}
|
||||
],
|
||||
"start": [
|
||||
DocCommentHighlightRules.getStartRule("doc-start"),
|
||||
{
|
||||
token : "comment", // multi line comment
|
||||
regex : "\\/\\*",
|
||||
next : "comment_regex_allowed"
|
||||
}, {
|
||||
token : "comment",
|
||||
regex : "\\/\\/",
|
||||
next : "line_comment_regex_allowed"
|
||||
}, {
|
||||
token: "string.regexp",
|
||||
regex: "\\/",
|
||||
next: "regex"
|
||||
}, {
|
||||
token : "text",
|
||||
regex : "\\s+|^$",
|
||||
next : "start"
|
||||
}, {
|
||||
token: "empty",
|
||||
regex: "",
|
||||
next: "no_regex"
|
||||
}
|
||||
],
|
||||
"regex": [
|
||||
{
|
||||
token: "regexp.keyword.operator",
|
||||
regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"
|
||||
}, {
|
||||
token: "string.regexp",
|
||||
regex: "/[sxngimy]*",
|
||||
next: "no_regex"
|
||||
}, {
|
||||
token : "invalid",
|
||||
regex: /\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/
|
||||
}, {
|
||||
token : "constant.language.escape",
|
||||
regex: /\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/
|
||||
}, {
|
||||
token : "constant.language.delimiter",
|
||||
regex: /\|/
|
||||
}, {
|
||||
token: "constant.language.escape",
|
||||
regex: /\[\^?/,
|
||||
next: "regex_character_class"
|
||||
}, {
|
||||
token: "empty",
|
||||
regex: "$",
|
||||
next: "no_regex"
|
||||
}, {
|
||||
defaultToken: "string.regexp"
|
||||
}
|
||||
],
|
||||
"regex_character_class": [
|
||||
{
|
||||
token: "regexp.charclass.keyword.operator",
|
||||
regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"
|
||||
}, {
|
||||
token: "constant.language.escape",
|
||||
regex: "]",
|
||||
next: "regex"
|
||||
}, {
|
||||
token: "constant.language.escape",
|
||||
regex: "-"
|
||||
}, {
|
||||
token: "empty",
|
||||
regex: "$",
|
||||
next: "no_regex"
|
||||
}, {
|
||||
defaultToken: "string.regexp.charachterclass"
|
||||
}
|
||||
],
|
||||
"function_arguments": [
|
||||
{
|
||||
token: "variable.parameter",
|
||||
regex: identifierRe
|
||||
}, {
|
||||
token: "punctuation.operator",
|
||||
regex: "[, ]+"
|
||||
}, {
|
||||
token: "punctuation.operator",
|
||||
regex: "$"
|
||||
}, {
|
||||
token: "empty",
|
||||
regex: "",
|
||||
next: "no_regex"
|
||||
}
|
||||
],
|
||||
"comment_regex_allowed" : [
|
||||
DocCommentHighlightRules.getTagRule(),
|
||||
{token : "comment", regex : "\\*\\/", next : "start"},
|
||||
{defaultToken : "comment", caseInsensitive: true}
|
||||
],
|
||||
"comment" : [
|
||||
DocCommentHighlightRules.getTagRule(),
|
||||
{token : "comment", regex : "\\*\\/", next : "no_regex"},
|
||||
{defaultToken : "comment", caseInsensitive: true}
|
||||
],
|
||||
"line_comment_regex_allowed" : [
|
||||
DocCommentHighlightRules.getTagRule(),
|
||||
{token : "comment", regex : "$|^", next : "start"},
|
||||
{defaultToken : "comment", caseInsensitive: true}
|
||||
],
|
||||
"line_comment" : [
|
||||
DocCommentHighlightRules.getTagRule(),
|
||||
{token : "comment", regex : "$|^", next : "no_regex"},
|
||||
{defaultToken : "comment", caseInsensitive: true}
|
||||
],
|
||||
"qqstring" : [
|
||||
{
|
||||
token : "constant.language.escape",
|
||||
regex : escapedRe
|
||||
}, {
|
||||
token : "string",
|
||||
regex : "\\\\$",
|
||||
next : "qqstring"
|
||||
}, {
|
||||
token : "string",
|
||||
regex : '"|$',
|
||||
next : "no_regex"
|
||||
}, {
|
||||
defaultToken: "string"
|
||||
}
|
||||
],
|
||||
"qstring" : [
|
||||
{
|
||||
token : "constant.language.escape",
|
||||
regex : escapedRe
|
||||
}, {
|
||||
token : "string",
|
||||
regex : "\\\\$",
|
||||
next : "qstring"
|
||||
}, {
|
||||
token : "string",
|
||||
regex : "'|$",
|
||||
next : "no_regex"
|
||||
}, {
|
||||
defaultToken: "string"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
if (!options || !options.noES6) {
|
||||
this.$rules.no_regex.unshift({
|
||||
regex: "[{}]", onMatch: function(val, state, stack) {
|
||||
this.next = val == "{" ? this.nextState : "";
|
||||
if (val == "{" && stack.length) {
|
||||
stack.unshift("start", state);
|
||||
return "paren";
|
||||
}
|
||||
if (val == "}" && stack.length) {
|
||||
stack.shift();
|
||||
this.next = stack.shift();
|
||||
if (this.next.indexOf("string") != -1)
|
||||
return "paren.quasi.end";
|
||||
}
|
||||
return val == "{" ? "paren.lparen" : "paren.rparen";
|
||||
},
|
||||
nextState: "start"
|
||||
}, {
|
||||
token : "string.quasi.start",
|
||||
regex : /`/,
|
||||
push : [{
|
||||
token : "constant.language.escape",
|
||||
regex : escapedRe
|
||||
}, {
|
||||
token : "paren.quasi.start",
|
||||
regex : /\${/,
|
||||
push : "start"
|
||||
}, {
|
||||
token : "string.quasi.end",
|
||||
regex : /`/,
|
||||
next : "pop"
|
||||
}, {
|
||||
defaultToken: "string.quasi"
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
this.embedRules(DocCommentHighlightRules, "doc-",
|
||||
[ DocCommentHighlightRules.getEndRule("no_regex") ]);
|
||||
|
||||
this.normalizeRules();
|
||||
};
|
||||
|
||||
oop.inherits(JavaScriptHighlightRules, TextHighlightRules);
|
||||
|
||||
exports.JavaScriptHighlightRules = JavaScriptHighlightRules;
|
||||
});
|
||||
|
||||
ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"], function(acequire, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var Range = acequire("../range").Range;
|
||||
|
||||
var MatchingBraceOutdent = function() {};
|
||||
|
||||
(function() {
|
||||
|
||||
this.checkOutdent = function(line, input) {
|
||||
if (! /^\s+$/.test(line))
|
||||
return false;
|
||||
|
||||
return /^\s*\}/.test(input);
|
||||
};
|
||||
|
||||
this.autoOutdent = function(doc, row) {
|
||||
var line = doc.getLine(row);
|
||||
var match = line.match(/^(\s*\})/);
|
||||
|
||||
if (!match) return 0;
|
||||
|
||||
var column = match[1].length;
|
||||
var openBracePos = doc.findMatchingBracket({row: row, column: column});
|
||||
|
||||
if (!openBracePos || openBracePos.row == row) return 0;
|
||||
|
||||
var indent = this.$getIndent(doc.getLine(openBracePos.row));
|
||||
doc.replace(new Range(row, 0, row, column-1), indent);
|
||||
};
|
||||
|
||||
this.$getIndent = function(line) {
|
||||
return line.match(/^\s*/)[0];
|
||||
};
|
||||
|
||||
}).call(MatchingBraceOutdent.prototype);
|
||||
|
||||
exports.MatchingBraceOutdent = MatchingBraceOutdent;
|
||||
});
|
||||
|
||||
ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"], function(acequire, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = acequire("../../lib/oop");
|
||||
var Behaviour = acequire("../behaviour").Behaviour;
|
||||
var TokenIterator = acequire("../../token_iterator").TokenIterator;
|
||||
var lang = acequire("../../lib/lang");
|
||||
|
||||
var SAFE_INSERT_IN_TOKENS =
|
||||
["text", "paren.rparen", "punctuation.operator"];
|
||||
var SAFE_INSERT_BEFORE_TOKENS =
|
||||
["text", "paren.rparen", "punctuation.operator", "comment"];
|
||||
|
||||
var context;
|
||||
var contextCache = {};
|
||||
var initContext = function(editor) {
|
||||
var id = -1;
|
||||
if (editor.multiSelect) {
|
||||
id = editor.selection.index;
|
||||
if (contextCache.rangeCount != editor.multiSelect.rangeCount)
|
||||
contextCache = {rangeCount: editor.multiSelect.rangeCount};
|
||||
}
|
||||
if (contextCache[id])
|
||||
return context = contextCache[id];
|
||||
context = contextCache[id] = {
|
||||
autoInsertedBrackets: 0,
|
||||
autoInsertedRow: -1,
|
||||
autoInsertedLineEnd: "",
|
||||
maybeInsertedBrackets: 0,
|
||||
maybeInsertedRow: -1,
|
||||
maybeInsertedLineStart: "",
|
||||
maybeInsertedLineEnd: ""
|
||||
};
|
||||
};
|
||||
|
||||
var CstyleBehaviour = function() {
|
||||
this.add("braces", "insertion", function(state, action, editor, session, text) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
if (text == '{') {
|
||||
initContext(editor);
|
||||
var selection = editor.getSelectionRange();
|
||||
var selected = session.doc.getTextRange(selection);
|
||||
if (selected !== "" && selected !== "{" && editor.getWrapBehavioursEnabled()) {
|
||||
return {
|
||||
text: '{' + selected + '}',
|
||||
selection: false
|
||||
};
|
||||
} else if (CstyleBehaviour.isSaneInsertion(editor, session)) {
|
||||
if (/[\]\}\)]/.test(line[cursor.column]) || editor.inMultiSelectMode) {
|
||||
CstyleBehaviour.recordAutoInsert(editor, session, "}");
|
||||
return {
|
||||
text: '{}',
|
||||
selection: [1, 1]
|
||||
};
|
||||
} else {
|
||||
CstyleBehaviour.recordMaybeInsert(editor, session, "{");
|
||||
return {
|
||||
text: '{',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if (text == '}') {
|
||||
initContext(editor);
|
||||
var rightChar = line.substring(cursor.column, cursor.column + 1);
|
||||
if (rightChar == '}') {
|
||||
var matching = session.$findOpeningBracket('}', {column: cursor.column + 1, row: cursor.row});
|
||||
if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) {
|
||||
CstyleBehaviour.popAutoInsertedClosing();
|
||||
return {
|
||||
text: '',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if (text == "\n" || text == "\r\n") {
|
||||
initContext(editor);
|
||||
var closing = "";
|
||||
if (CstyleBehaviour.isMaybeInsertedClosing(cursor, line)) {
|
||||
closing = lang.stringRepeat("}", context.maybeInsertedBrackets);
|
||||
CstyleBehaviour.clearMaybeInsertedClosing();
|
||||
}
|
||||
var rightChar = line.substring(cursor.column, cursor.column + 1);
|
||||
if (rightChar === '}') {
|
||||
var openBracePos = session.findMatchingBracket({row: cursor.row, column: cursor.column+1}, '}');
|
||||
if (!openBracePos)
|
||||
return null;
|
||||
var next_indent = this.$getIndent(session.getLine(openBracePos.row));
|
||||
} else if (closing) {
|
||||
var next_indent = this.$getIndent(line);
|
||||
} else {
|
||||
CstyleBehaviour.clearMaybeInsertedClosing();
|
||||
return;
|
||||
}
|
||||
var indent = next_indent + session.getTabString();
|
||||
|
||||
return {
|
||||
text: '\n' + indent + '\n' + next_indent + closing,
|
||||
selection: [1, indent.length, 1, indent.length]
|
||||
};
|
||||
} else {
|
||||
CstyleBehaviour.clearMaybeInsertedClosing();
|
||||
}
|
||||
});
|
||||
|
||||
this.add("braces", "deletion", function(state, action, editor, session, range) {
|
||||
var selected = session.doc.getTextRange(range);
|
||||
if (!range.isMultiLine() && selected == '{') {
|
||||
initContext(editor);
|
||||
var line = session.doc.getLine(range.start.row);
|
||||
var rightChar = line.substring(range.end.column, range.end.column + 1);
|
||||
if (rightChar == '}') {
|
||||
range.end.column++;
|
||||
return range;
|
||||
} else {
|
||||
context.maybeInsertedBrackets--;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("parens", "insertion", function(state, action, editor, session, text) {
|
||||
if (text == '(') {
|
||||
initContext(editor);
|
||||
var selection = editor.getSelectionRange();
|
||||
var selected = session.doc.getTextRange(selection);
|
||||
if (selected !== "" && editor.getWrapBehavioursEnabled()) {
|
||||
return {
|
||||
text: '(' + selected + ')',
|
||||
selection: false
|
||||
};
|
||||
} else if (CstyleBehaviour.isSaneInsertion(editor, session)) {
|
||||
CstyleBehaviour.recordAutoInsert(editor, session, ")");
|
||||
return {
|
||||
text: '()',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
} else if (text == ')') {
|
||||
initContext(editor);
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
var rightChar = line.substring(cursor.column, cursor.column + 1);
|
||||
if (rightChar == ')') {
|
||||
var matching = session.$findOpeningBracket(')', {column: cursor.column + 1, row: cursor.row});
|
||||
if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) {
|
||||
CstyleBehaviour.popAutoInsertedClosing();
|
||||
return {
|
||||
text: '',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("parens", "deletion", function(state, action, editor, session, range) {
|
||||
var selected = session.doc.getTextRange(range);
|
||||
if (!range.isMultiLine() && selected == '(') {
|
||||
initContext(editor);
|
||||
var line = session.doc.getLine(range.start.row);
|
||||
var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
|
||||
if (rightChar == ')') {
|
||||
range.end.column++;
|
||||
return range;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("brackets", "insertion", function(state, action, editor, session, text) {
|
||||
if (text == '[') {
|
||||
initContext(editor);
|
||||
var selection = editor.getSelectionRange();
|
||||
var selected = session.doc.getTextRange(selection);
|
||||
if (selected !== "" && editor.getWrapBehavioursEnabled()) {
|
||||
return {
|
||||
text: '[' + selected + ']',
|
||||
selection: false
|
||||
};
|
||||
} else if (CstyleBehaviour.isSaneInsertion(editor, session)) {
|
||||
CstyleBehaviour.recordAutoInsert(editor, session, "]");
|
||||
return {
|
||||
text: '[]',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
} else if (text == ']') {
|
||||
initContext(editor);
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
var rightChar = line.substring(cursor.column, cursor.column + 1);
|
||||
if (rightChar == ']') {
|
||||
var matching = session.$findOpeningBracket(']', {column: cursor.column + 1, row: cursor.row});
|
||||
if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) {
|
||||
CstyleBehaviour.popAutoInsertedClosing();
|
||||
return {
|
||||
text: '',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("brackets", "deletion", function(state, action, editor, session, range) {
|
||||
var selected = session.doc.getTextRange(range);
|
||||
if (!range.isMultiLine() && selected == '[') {
|
||||
initContext(editor);
|
||||
var line = session.doc.getLine(range.start.row);
|
||||
var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
|
||||
if (rightChar == ']') {
|
||||
range.end.column++;
|
||||
return range;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("string_dquotes", "insertion", function(state, action, editor, session, text) {
|
||||
if (text == '"' || text == "'") {
|
||||
initContext(editor);
|
||||
var quote = text;
|
||||
var selection = editor.getSelectionRange();
|
||||
var selected = session.doc.getTextRange(selection);
|
||||
if (selected !== "" && selected !== "'" && selected != '"' && editor.getWrapBehavioursEnabled()) {
|
||||
return {
|
||||
text: quote + selected + quote,
|
||||
selection: false
|
||||
};
|
||||
} else {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
var leftChar = line.substring(cursor.column-1, cursor.column);
|
||||
if (leftChar == '\\') {
|
||||
return null;
|
||||
}
|
||||
var tokens = session.getTokens(selection.start.row);
|
||||
var col = 0, token;
|
||||
var quotepos = -1; // Track whether we're inside an open quote.
|
||||
|
||||
for (var x = 0; x < tokens.length; x++) {
|
||||
token = tokens[x];
|
||||
if (token.type == "string") {
|
||||
quotepos = -1;
|
||||
} else if (quotepos < 0) {
|
||||
quotepos = token.value.indexOf(quote);
|
||||
}
|
||||
if ((token.value.length + col) > selection.start.column) {
|
||||
break;
|
||||
}
|
||||
col += tokens[x].value.length;
|
||||
}
|
||||
if (!token || (quotepos < 0 && token.type !== "comment" && (token.type !== "string" || ((selection.start.column !== token.value.length+col-1) && token.value.lastIndexOf(quote) === token.value.length-1)))) {
|
||||
if (!CstyleBehaviour.isSaneInsertion(editor, session))
|
||||
return;
|
||||
return {
|
||||
text: quote + quote,
|
||||
selection: [1,1]
|
||||
};
|
||||
} else if (token && token.type === "string") {
|
||||
var rightChar = line.substring(cursor.column, cursor.column + 1);
|
||||
if (rightChar == quote) {
|
||||
return {
|
||||
text: '',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("string_dquotes", "deletion", function(state, action, editor, session, range) {
|
||||
var selected = session.doc.getTextRange(range);
|
||||
if (!range.isMultiLine() && (selected == '"' || selected == "'")) {
|
||||
initContext(editor);
|
||||
var line = session.doc.getLine(range.start.row);
|
||||
var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
|
||||
if (rightChar == selected) {
|
||||
range.end.column++;
|
||||
return range;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
CstyleBehaviour.isSaneInsertion = function(editor, session) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var iterator = new TokenIterator(session, cursor.row, cursor.column);
|
||||
if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) {
|
||||
var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1);
|
||||
if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS))
|
||||
return false;
|
||||
}
|
||||
iterator.stepForward();
|
||||
return iterator.getCurrentTokenRow() !== cursor.row ||
|
||||
this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS);
|
||||
};
|
||||
|
||||
CstyleBehaviour.$matchTokenType = function(token, types) {
|
||||
return types.indexOf(token.type || token) > -1;
|
||||
};
|
||||
|
||||
CstyleBehaviour.recordAutoInsert = function(editor, session, bracket) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
if (!this.isAutoInsertedClosing(cursor, line, context.autoInsertedLineEnd[0]))
|
||||
context.autoInsertedBrackets = 0;
|
||||
context.autoInsertedRow = cursor.row;
|
||||
context.autoInsertedLineEnd = bracket + line.substr(cursor.column);
|
||||
context.autoInsertedBrackets++;
|
||||
};
|
||||
|
||||
CstyleBehaviour.recordMaybeInsert = function(editor, session, bracket) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
if (!this.isMaybeInsertedClosing(cursor, line))
|
||||
context.maybeInsertedBrackets = 0;
|
||||
context.maybeInsertedRow = cursor.row;
|
||||
context.maybeInsertedLineStart = line.substr(0, cursor.column) + bracket;
|
||||
context.maybeInsertedLineEnd = line.substr(cursor.column);
|
||||
context.maybeInsertedBrackets++;
|
||||
};
|
||||
|
||||
CstyleBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) {
|
||||
return context.autoInsertedBrackets > 0 &&
|
||||
cursor.row === context.autoInsertedRow &&
|
||||
bracket === context.autoInsertedLineEnd[0] &&
|
||||
line.substr(cursor.column) === context.autoInsertedLineEnd;
|
||||
};
|
||||
|
||||
CstyleBehaviour.isMaybeInsertedClosing = function(cursor, line) {
|
||||
return context.maybeInsertedBrackets > 0 &&
|
||||
cursor.row === context.maybeInsertedRow &&
|
||||
line.substr(cursor.column) === context.maybeInsertedLineEnd &&
|
||||
line.substr(0, cursor.column) == context.maybeInsertedLineStart;
|
||||
};
|
||||
|
||||
CstyleBehaviour.popAutoInsertedClosing = function() {
|
||||
context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1);
|
||||
context.autoInsertedBrackets--;
|
||||
};
|
||||
|
||||
CstyleBehaviour.clearMaybeInsertedClosing = function() {
|
||||
if (context) {
|
||||
context.maybeInsertedBrackets = 0;
|
||||
context.maybeInsertedRow = -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
oop.inherits(CstyleBehaviour, Behaviour);
|
||||
|
||||
exports.CstyleBehaviour = CstyleBehaviour;
|
||||
});
|
||||
|
||||
ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"], function(acequire, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = acequire("../../lib/oop");
|
||||
var Range = acequire("../../range").Range;
|
||||
var BaseFoldMode = acequire("./fold_mode").FoldMode;
|
||||
|
||||
var FoldMode = exports.FoldMode = function(commentRegex) {
|
||||
if (commentRegex) {
|
||||
this.foldingStartMarker = new RegExp(
|
||||
this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start)
|
||||
);
|
||||
this.foldingStopMarker = new RegExp(
|
||||
this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end)
|
||||
);
|
||||
}
|
||||
};
|
||||
oop.inherits(FoldMode, BaseFoldMode);
|
||||
|
||||
(function() {
|
||||
|
||||
this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/;
|
||||
this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/;
|
||||
|
||||
this.getFoldWidgetRange = function(session, foldStyle, row, forceMultiline) {
|
||||
var line = session.getLine(row);
|
||||
var match = line.match(this.foldingStartMarker);
|
||||
if (match) {
|
||||
var i = match.index;
|
||||
|
||||
if (match[1])
|
||||
return this.openingBracketBlock(session, match[1], row, i);
|
||||
|
||||
var range = session.getCommentFoldRange(row, i + match[0].length, 1);
|
||||
|
||||
if (range && !range.isMultiLine()) {
|
||||
if (forceMultiline) {
|
||||
range = this.getSectionRange(session, row);
|
||||
} else if (foldStyle != "all")
|
||||
range = null;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
if (foldStyle === "markbegin")
|
||||
return;
|
||||
|
||||
var match = line.match(this.foldingStopMarker);
|
||||
if (match) {
|
||||
var i = match.index + match[0].length;
|
||||
|
||||
if (match[1])
|
||||
return this.closingBracketBlock(session, match[1], row, i);
|
||||
|
||||
return session.getCommentFoldRange(row, i, -1);
|
||||
}
|
||||
};
|
||||
|
||||
this.getSectionRange = function(session, row) {
|
||||
var line = session.getLine(row);
|
||||
var startIndent = line.search(/\S/);
|
||||
var startRow = row;
|
||||
var startColumn = line.length;
|
||||
row = row + 1;
|
||||
var endRow = row;
|
||||
var maxRow = session.getLength();
|
||||
while (++row < maxRow) {
|
||||
line = session.getLine(row);
|
||||
var indent = line.search(/\S/);
|
||||
if (indent === -1)
|
||||
continue;
|
||||
if (startIndent > indent)
|
||||
break;
|
||||
var subRange = this.getFoldWidgetRange(session, "all", row);
|
||||
|
||||
if (subRange) {
|
||||
if (subRange.start.row <= startRow) {
|
||||
break;
|
||||
} else if (subRange.isMultiLine()) {
|
||||
row = subRange.end.row;
|
||||
} else if (startIndent == indent) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
endRow = row;
|
||||
}
|
||||
|
||||
return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
|
||||
};
|
||||
|
||||
}).call(FoldMode.prototype);
|
||||
|
||||
});
|
||||
|
||||
ace.define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"], function(acequire, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = acequire("../lib/oop");
|
||||
var TextMode = acequire("./text").Mode;
|
||||
var JavaScriptHighlightRules = acequire("./javascript_highlight_rules").JavaScriptHighlightRules;
|
||||
var MatchingBraceOutdent = acequire("./matching_brace_outdent").MatchingBraceOutdent;
|
||||
var Range = acequire("../range").Range;
|
||||
var WorkerClient = acequire("../worker/worker_client").WorkerClient;
|
||||
var CstyleBehaviour = acequire("./behaviour/cstyle").CstyleBehaviour;
|
||||
var CStyleFoldMode = acequire("./folding/cstyle").FoldMode;
|
||||
|
||||
var Mode = function() {
|
||||
this.HighlightRules = JavaScriptHighlightRules;
|
||||
|
||||
this.$outdent = new MatchingBraceOutdent();
|
||||
this.$behaviour = new CstyleBehaviour();
|
||||
this.foldingRules = new CStyleFoldMode();
|
||||
};
|
||||
oop.inherits(Mode, TextMode);
|
||||
|
||||
(function() {
|
||||
|
||||
this.lineCommentStart = "//";
|
||||
this.blockComment = {start: "/*", end: "*/"};
|
||||
|
||||
this.getNextLineIndent = function(state, line, tab) {
|
||||
var indent = this.$getIndent(line);
|
||||
|
||||
var tokenizedLine = this.getTokenizer().getLineTokens(line, state);
|
||||
var tokens = tokenizedLine.tokens;
|
||||
var endState = tokenizedLine.state;
|
||||
|
||||
if (tokens.length && tokens[tokens.length-1].type == "comment") {
|
||||
return indent;
|
||||
}
|
||||
|
||||
if (state == "start" || state == "no_regex") {
|
||||
var match = line.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);
|
||||
if (match) {
|
||||
indent += tab;
|
||||
}
|
||||
} else if (state == "doc-start") {
|
||||
if (endState == "start" || endState == "no_regex") {
|
||||
return "";
|
||||
}
|
||||
var match = line.match(/^\s*(\/?)\*/);
|
||||
if (match) {
|
||||
if (match[1]) {
|
||||
indent += " ";
|
||||
}
|
||||
indent += "* ";
|
||||
}
|
||||
}
|
||||
|
||||
return indent;
|
||||
};
|
||||
|
||||
this.checkOutdent = function(state, line, input) {
|
||||
return this.$outdent.checkOutdent(line, input);
|
||||
};
|
||||
|
||||
this.autoOutdent = function(state, doc, row) {
|
||||
this.$outdent.autoOutdent(doc, row);
|
||||
};
|
||||
|
||||
// this.createWorker = function(session) {
|
||||
// var worker = new WorkerClient(["ace"], "ace/mode/javascript_worker", "JavaScriptWorker");
|
||||
// worker.attachToDocument(session.getDocument());
|
||||
//
|
||||
// worker.on("jslint", function(results) {
|
||||
// session.setAnnotations(results.data);
|
||||
// });
|
||||
//
|
||||
// worker.on("terminate", function() {
|
||||
// session.clearAnnotations();
|
||||
// });
|
||||
//
|
||||
// return worker;
|
||||
// };
|
||||
|
||||
this.$id = "ace/mode/javascript";
|
||||
}).call(Mode.prototype);
|
||||
|
||||
exports.Mode = Mode;
|
||||
});
|
||||
@@ -15,26 +15,24 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.account {
|
||||
max-height: 36px;
|
||||
padding: 4px 0 0 0;
|
||||
line-height: 32px;
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
.details {
|
||||
.name {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
text-transform: uppercase;
|
||||
padding: 0 0 0 1em;
|
||||
}
|
||||
|
||||
.image {
|
||||
display: inline-block;
|
||||
max-height: 32px;
|
||||
}
|
||||
|
||||
.name {
|
||||
line-height: 32px;
|
||||
vertical-align: top;
|
||||
text-transform: uppercase;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
margin: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@@ -50,3 +48,8 @@
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menuItem {
|
||||
min-height: 0 !important;
|
||||
line-height: inherit !important;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { MenuItem } from 'material-ui';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import AutoComplete from '../AutoComplete';
|
||||
import IdentityIcon from '../../IdentityIcon';
|
||||
@@ -24,57 +25,82 @@ import IdentityName from '../../IdentityName';
|
||||
import styles from './addressSelect.css';
|
||||
|
||||
export default class AddressSelect extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
disabled: PropTypes.bool,
|
||||
accounts: PropTypes.object,
|
||||
contacts: PropTypes.object,
|
||||
contracts: PropTypes.object,
|
||||
label: PropTypes.string,
|
||||
hint: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
tokens: PropTypes.object,
|
||||
onChange: PropTypes.func.isRequired
|
||||
onChange: PropTypes.func.isRequired,
|
||||
allowInput: PropTypes.bool
|
||||
}
|
||||
|
||||
state = {
|
||||
entries: {},
|
||||
addresses: [],
|
||||
value: ''
|
||||
}
|
||||
|
||||
entriesFromProps (props = this.props) {
|
||||
const { accounts, contacts, contracts } = props;
|
||||
const entries = Object.assign({}, accounts || {}, contacts || {}, contracts || {});
|
||||
return entries;
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
const { accounts, contacts, value } = this.props;
|
||||
const entries = Object.assign({}, accounts || {}, contacts || {});
|
||||
this.setState({ entries, value });
|
||||
const { value } = this.props;
|
||||
const entries = this.entriesFromProps();
|
||||
const addresses = Object.keys(entries).sort();
|
||||
|
||||
this.setState({ entries, addresses, value });
|
||||
}
|
||||
|
||||
componentWillReceiveProps (newProps) {
|
||||
const { accounts, contacts } = newProps;
|
||||
const entries = Object.assign({}, accounts || {}, contacts || {});
|
||||
this.setState({ entries });
|
||||
const entries = this.entriesFromProps();
|
||||
const addresses = Object.keys(entries).sort();
|
||||
|
||||
if (!isEqual(addresses, this.state.addresses)) {
|
||||
this.setState({ entries, addresses });
|
||||
}
|
||||
|
||||
if (newProps.value !== this.props.value) {
|
||||
this.setState({ value: newProps.value });
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { disabled, error, hint, label } = this.props;
|
||||
const { entries } = this.state;
|
||||
const value = this.getSearchText();
|
||||
const { allowInput, disabled, error, hint, label } = this.props;
|
||||
const { entries, value } = this.state;
|
||||
|
||||
const searchText = this.getSearchText();
|
||||
const icon = this.renderIdentityIcon(value);
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<AutoComplete
|
||||
className={ (error || !value) ? '' : styles.paddedInput }
|
||||
className={ !icon ? '' : styles.paddedInput }
|
||||
disabled={ disabled }
|
||||
label={ label }
|
||||
hint={ hint ? `search for ${hint}` : 'search for an address' }
|
||||
error={ error }
|
||||
onChange={ this.onChange }
|
||||
value={ value }
|
||||
onBlur={ this.onBlur }
|
||||
onUpdateInput={ allowInput && this.onUpdateInput }
|
||||
value={ searchText }
|
||||
filter={ this.handleFilter }
|
||||
entries={ entries }
|
||||
entry={ this.getEntry() || {} }
|
||||
renderItem={ this.renderItem }
|
||||
/>
|
||||
|
||||
{ this.renderIdentityIcon(value) }
|
||||
{ icon }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -82,7 +108,7 @@ export default class AddressSelect extends Component {
|
||||
renderIdentityIcon (inputValue) {
|
||||
const { error, value } = this.props;
|
||||
|
||||
if (error || !inputValue) {
|
||||
if (error || !inputValue || value.length !== 42) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -96,29 +122,28 @@ export default class AddressSelect extends Component {
|
||||
|
||||
renderItem = (entry) => {
|
||||
return {
|
||||
text: entry.address,
|
||||
value: this.renderSelectEntry(entry)
|
||||
text: entry.name && entry.name.toUpperCase() || entry.address,
|
||||
value: this.renderSelectEntry(entry),
|
||||
address: entry.address
|
||||
};
|
||||
}
|
||||
|
||||
renderSelectEntry = (entry) => {
|
||||
const item = (
|
||||
<div className={ styles.account }>
|
||||
<div className={ styles.image }>
|
||||
<IdentityIcon
|
||||
inline center
|
||||
address={ entry.address } />
|
||||
</div>
|
||||
<div className={ styles.details }>
|
||||
<div className={ styles.name }>
|
||||
<IdentityName address={ entry.address } />
|
||||
</div>
|
||||
</div>
|
||||
<IdentityIcon
|
||||
className={ styles.image }
|
||||
inline center
|
||||
address={ entry.address } />
|
||||
<IdentityName
|
||||
className={ styles.name }
|
||||
address={ entry.address } />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
className={ styles.menuItem }
|
||||
key={ entry.address }
|
||||
value={ entry.address }
|
||||
label={ item }>
|
||||
@@ -129,32 +154,48 @@ export default class AddressSelect extends Component {
|
||||
|
||||
getSearchText () {
|
||||
const entry = this.getEntry();
|
||||
if (!entry) return '';
|
||||
const { value } = this.state;
|
||||
|
||||
return entry.name ? entry.name.toUpperCase() : '';
|
||||
return entry && entry.name
|
||||
? entry.name.toUpperCase()
|
||||
: value;
|
||||
}
|
||||
|
||||
getEntry () {
|
||||
const { value } = this.props;
|
||||
if (!value) return '';
|
||||
|
||||
const { entries } = this.state;
|
||||
return entries[value];
|
||||
const { entries, value } = this.state;
|
||||
return value ? entries[value] : null;
|
||||
}
|
||||
|
||||
handleFilter = (searchText, address) => {
|
||||
handleFilter = (searchText, name, item) => {
|
||||
const { address } = item;
|
||||
const entry = this.state.entries[address];
|
||||
const lowCaseSearch = searchText.toLowerCase();
|
||||
|
||||
return [ entry.name, entry.address ]
|
||||
return [entry.name, entry.address]
|
||||
.some(text => text.toLowerCase().indexOf(lowCaseSearch) !== -1);
|
||||
}
|
||||
|
||||
onChange = (entry, empty) => {
|
||||
const { allowInput } = this.props;
|
||||
const { value } = this.state;
|
||||
|
||||
const address = entry && entry.address
|
||||
? entry.address
|
||||
: (empty ? '' : this.state.value);
|
||||
: ((empty && !allowInput) ? '' : value);
|
||||
|
||||
this.props.onChange(null, address);
|
||||
}
|
||||
|
||||
onUpdateInput = (query, choices) => {
|
||||
const { api } = this.context;
|
||||
|
||||
const address = query.trim();
|
||||
|
||||
if (!/^0x/.test(address) && api.util.isAddressValid(`0x${address}`)) {
|
||||
const checksumed = api.util.toChecksumAddress(`0x${address}`);
|
||||
return this.props.onChange(null, checksumed);
|
||||
}
|
||||
|
||||
this.props.onChange(null, address);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import keycode from 'keycode';
|
||||
import { MenuItem, AutoComplete as MUIAutoComplete } from 'material-ui';
|
||||
import { PopoverAnimationVertical } from 'material-ui/Popover';
|
||||
|
||||
export default class AutoComplete extends Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onUpdateInput: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
label: PropTypes.string,
|
||||
hint: PropTypes.string,
|
||||
@@ -39,11 +41,12 @@ export default class AutoComplete extends Component {
|
||||
state = {
|
||||
lastChangedValue: undefined,
|
||||
entry: null,
|
||||
open: false
|
||||
open: false,
|
||||
fakeBlur: false
|
||||
}
|
||||
|
||||
render () {
|
||||
const { disabled, error, hint, label, value, className, filter } = this.props;
|
||||
const { disabled, error, hint, label, value, className, filter, onUpdateInput } = this.props;
|
||||
const { open } = this.state;
|
||||
|
||||
return (
|
||||
@@ -54,11 +57,11 @@ export default class AutoComplete extends Component {
|
||||
hintText={ hint }
|
||||
errorText={ error }
|
||||
onNewRequest={ this.onChange }
|
||||
onUpdateInput={ onUpdateInput }
|
||||
searchText={ value }
|
||||
onFocus={ this.onFocus }
|
||||
onBlur={ this.onBlur }
|
||||
animation={ PopoverAnimationVertical }
|
||||
|
||||
filter={ filter }
|
||||
popoverProps={ { open } }
|
||||
openOnFocus
|
||||
@@ -66,6 +69,9 @@ export default class AutoComplete extends Component {
|
||||
fullWidth
|
||||
floatingLabelFixed
|
||||
dataSource={ this.getDataSource() }
|
||||
menuProps={ { maxHeight: 400 } }
|
||||
ref='muiAutocomplete'
|
||||
onKeyDown={ this.onKeyDown }
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -90,6 +96,30 @@ export default class AutoComplete extends Component {
|
||||
}));
|
||||
}
|
||||
|
||||
onKeyDown = (event) => {
|
||||
const { muiAutocomplete } = this.refs;
|
||||
|
||||
switch (keycode(event)) {
|
||||
case 'down':
|
||||
const { menu } = muiAutocomplete.refs;
|
||||
menu.handleKeyDown(event);
|
||||
this.setState({ fakeBlur: true });
|
||||
break;
|
||||
|
||||
case 'enter':
|
||||
case 'tab':
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.which = 'useless';
|
||||
|
||||
const e = new CustomEvent('down');
|
||||
e.which = 40;
|
||||
|
||||
muiAutocomplete.handleKeyDown(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onChange = (item, idx) => {
|
||||
if (idx === -1) {
|
||||
return;
|
||||
@@ -107,12 +137,23 @@ export default class AutoComplete extends Component {
|
||||
this.setState({ entry, open: false });
|
||||
}
|
||||
|
||||
onBlur = () => {
|
||||
window.setTimeout(() => {
|
||||
const { entry } = this.state;
|
||||
onBlur = (event) => {
|
||||
const { onUpdateInput } = this.props;
|
||||
|
||||
this.handleOnChange(entry);
|
||||
}, 100);
|
||||
// TODO: Handle blur gracefully where we use onUpdateInput (currently replaces
|
||||
// input where text is allowed with the last selected value from the dropdown)
|
||||
if (!onUpdateInput) {
|
||||
window.setTimeout(() => {
|
||||
const { entry, fakeBlur } = this.state;
|
||||
|
||||
if (fakeBlur) {
|
||||
this.setState({ fakeBlur: false });
|
||||
return;
|
||||
}
|
||||
|
||||
this.handleOnChange(entry);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
onFocus = () => {
|
||||
@@ -131,5 +172,4 @@ export default class AutoComplete extends Component {
|
||||
this.props.onChange(value, empty);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,18 +14,14 @@
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.inputselect {
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.inputselect svg {
|
||||
padding-right: 84px;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
position: absolute !important;
|
||||
top: 38px;
|
||||
right: 0;
|
||||
display: inline-block !important;
|
||||
width: auto !important;
|
||||
.copy {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
@@ -15,12 +15,21 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { TextField } from 'material-ui';
|
||||
|
||||
import CopyToClipboard from '../../CopyToClipboard';
|
||||
|
||||
import styles from './input.css';
|
||||
|
||||
// TODO: duplicated in Select
|
||||
const UNDERLINE_DISABLED = {
|
||||
borderColor: 'rgba(255, 255, 255, 0.298039)' // 'transparent' // 'rgba(255, 255, 255, 0.298039)'
|
||||
borderBottom: 'dotted 2px',
|
||||
borderColor: 'rgba(255, 255, 255, 0.125)' // 'transparent' // 'rgba(255, 255, 255, 0.298039)'
|
||||
};
|
||||
|
||||
const UNDERLINE_READONLY = {
|
||||
...UNDERLINE_DISABLED,
|
||||
cursor: 'text'
|
||||
};
|
||||
|
||||
const UNDERLINE_NORMAL = {
|
||||
@@ -34,6 +43,12 @@ export default class Input extends Component {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
readOnly: PropTypes.bool,
|
||||
allowCopy: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.bool
|
||||
]),
|
||||
floatCopy: PropTypes.bool,
|
||||
error: PropTypes.string,
|
||||
hint: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
@@ -45,13 +60,18 @@ export default class Input extends Component {
|
||||
rows: PropTypes.number,
|
||||
type: PropTypes.string,
|
||||
submitOnBlur: PropTypes.bool,
|
||||
hideUnderline: PropTypes.bool,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.number, PropTypes.string
|
||||
])
|
||||
}
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
submitOnBlur: true
|
||||
submitOnBlur: true,
|
||||
readOnly: false,
|
||||
allowCopy: false,
|
||||
hideUnderline: false,
|
||||
floatCopy: false
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -66,31 +86,92 @@ export default class Input extends Component {
|
||||
|
||||
render () {
|
||||
const { value } = this.state;
|
||||
const { children, className, disabled, error, label, hint, multiLine, rows, type } = this.props;
|
||||
const { children, className, hideUnderline, disabled, error, label, hint, multiLine, rows, type } = this.props;
|
||||
|
||||
const readOnly = this.props.readOnly || disabled;
|
||||
|
||||
const inputStyle = { overflow: 'hidden' };
|
||||
const textFieldStyle = {};
|
||||
|
||||
if (readOnly) {
|
||||
inputStyle.cursor = 'text';
|
||||
}
|
||||
|
||||
if (hideUnderline && !hint) {
|
||||
textFieldStyle.height = 'initial';
|
||||
}
|
||||
|
||||
return (
|
||||
<TextField
|
||||
autoComplete='off'
|
||||
className={ className }
|
||||
disabled={ disabled }
|
||||
errorText={ error }
|
||||
floatingLabelFixed
|
||||
floatingLabelText={ label }
|
||||
fullWidth
|
||||
hintText={ hint }
|
||||
multiLine={ multiLine }
|
||||
name={ NAME_ID }
|
||||
id={ NAME_ID }
|
||||
rows={ rows }
|
||||
type={ type || 'text' }
|
||||
underlineDisabledStyle={ UNDERLINE_DISABLED }
|
||||
underlineStyle={ UNDERLINE_NORMAL }
|
||||
value={ value }
|
||||
onBlur={ this.onBlur }
|
||||
onChange={ this.onChange }
|
||||
onKeyDown={ this.onKeyDown }>
|
||||
{ children }
|
||||
</TextField>
|
||||
<div className={ styles.container }>
|
||||
{ this.renderCopyButton() }
|
||||
<TextField
|
||||
autoComplete='off'
|
||||
className={ className }
|
||||
style={ textFieldStyle }
|
||||
|
||||
readOnly={ readOnly }
|
||||
|
||||
errorText={ error }
|
||||
floatingLabelFixed
|
||||
floatingLabelText={ label }
|
||||
fullWidth
|
||||
hintText={ hint }
|
||||
multiLine={ multiLine }
|
||||
name={ NAME_ID }
|
||||
id={ NAME_ID }
|
||||
rows={ rows }
|
||||
type={ type || 'text' }
|
||||
underlineDisabledStyle={ UNDERLINE_DISABLED }
|
||||
underlineStyle={ readOnly ? UNDERLINE_READONLY : UNDERLINE_NORMAL }
|
||||
underlineFocusStyle={ readOnly ? { display: 'none' } : null }
|
||||
underlineShow={ !hideUnderline }
|
||||
value={ value }
|
||||
onBlur={ this.onBlur }
|
||||
onChange={ this.onChange }
|
||||
onKeyDown={ this.onKeyDown }
|
||||
inputStyle={ inputStyle }
|
||||
>
|
||||
{ children }
|
||||
</TextField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCopyButton () {
|
||||
const { allowCopy, hideUnderline, label, hint, floatCopy } = this.props;
|
||||
const { value } = this.state;
|
||||
|
||||
if (!allowCopy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const style = {
|
||||
marginBottom: 13
|
||||
};
|
||||
|
||||
const text = typeof allowCopy === 'string'
|
||||
? allowCopy
|
||||
: value;
|
||||
|
||||
if (hideUnderline && !label) {
|
||||
style.marginBottom = 2;
|
||||
} else if (label && !hint) {
|
||||
style.marginBottom = 4;
|
||||
} else if (label && hint) {
|
||||
style.marginBottom = 10;
|
||||
}
|
||||
|
||||
if (floatCopy) {
|
||||
style.position = 'absolute';
|
||||
style.left = -24;
|
||||
style.bottom = style.marginBottom;
|
||||
style.marginBottom = 0;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.copy } style={ style }>
|
||||
<CopyToClipboard data={ text } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -130,8 +211,6 @@ export default class Input extends Component {
|
||||
}
|
||||
|
||||
setValue (value) {
|
||||
this.setState({
|
||||
value
|
||||
});
|
||||
this.setState({ value });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,16 @@
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
.icon,
|
||||
.iconDisabled {
|
||||
position: absolute;
|
||||
top: 35px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.iconDisabled {
|
||||
left: 24px;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import { bindActionCreators } from 'redux';
|
||||
|
||||
import Input from '../Input';
|
||||
import IdentityIcon from '../../IdentityIcon';
|
||||
import util from '../../../api/util';
|
||||
|
||||
import styles from './inputAddress.css';
|
||||
|
||||
@@ -38,44 +39,17 @@ class InputAddress extends Component {
|
||||
onSubmit: PropTypes.func
|
||||
};
|
||||
|
||||
state = {
|
||||
isEmpty: false
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
const { value, text, accountsInfo, tokens } = this.props;
|
||||
|
||||
const account = accountsInfo[value] || tokens[value];
|
||||
const hasAccount = account && (!account.meta || !account.meta.deleted);
|
||||
const inputValue = text && hasAccount ? account.name : value;
|
||||
const isEmpty = (!inputValue || inputValue.length === 0);
|
||||
|
||||
this.setState({ isEmpty });
|
||||
}
|
||||
|
||||
componentWillReceiveProps (newProps) {
|
||||
const { value, text } = newProps;
|
||||
|
||||
if (value === this.props.value && text === this.props.text) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inputValue = text || value;
|
||||
const isEmpty = (!inputValue || inputValue.length === 0);
|
||||
|
||||
this.setState({ isEmpty });
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, disabled, error, label, hint, value, text, onSubmit, accountsInfo, tokens } = this.props;
|
||||
const { isEmpty } = this.state;
|
||||
|
||||
const classes = [ className ];
|
||||
classes.push(isEmpty ? styles.inputEmpty : styles.input);
|
||||
|
||||
const account = accountsInfo[value] || tokens[value];
|
||||
const hasAccount = account && (!account.meta || !account.meta.deleted);
|
||||
|
||||
const icon = this.renderIcon();
|
||||
|
||||
const classes = [ className ];
|
||||
classes.push(!icon ? styles.inputEmpty : styles.input);
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<Input
|
||||
@@ -86,21 +60,23 @@ class InputAddress extends Component {
|
||||
error={ error }
|
||||
value={ text && hasAccount ? account.name : value }
|
||||
onChange={ this.handleInputChange }
|
||||
onSubmit={ onSubmit } />
|
||||
{ this.renderIcon() }
|
||||
onSubmit={ onSubmit }
|
||||
allowCopy={ disabled ? value : false }
|
||||
/>
|
||||
{ icon }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderIcon () {
|
||||
const { value } = this.props;
|
||||
const { value, disabled } = this.props;
|
||||
|
||||
if (!value || !value.length) {
|
||||
if (!value || !value.length || !util.isAddressValid(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.icon }>
|
||||
<div className={ disabled ? styles.iconDisabled : styles.icon }>
|
||||
<IdentityIcon
|
||||
inline center
|
||||
address={ value } />
|
||||
@@ -112,6 +88,11 @@ class InputAddress extends Component {
|
||||
const isEmpty = (value.length === 0);
|
||||
|
||||
this.setState({ isEmpty });
|
||||
|
||||
if (!/^0x/.test(value) && util.isAddressValid(`0x${value}`)) {
|
||||
return this.props.onChange(event, `0x${value}`);
|
||||
}
|
||||
|
||||
this.props.onChange(event, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,103 +17,46 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { Toggle } from 'material-ui';
|
||||
|
||||
import AddressSelect from '../AddressSelect';
|
||||
import InputAddress from '../InputAddress';
|
||||
|
||||
import styles from './inputAddressSelect.css';
|
||||
|
||||
class InputAddressSelect extends Component {
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object,
|
||||
contacts: PropTypes.object,
|
||||
disabled: PropTypes.bool,
|
||||
editing: PropTypes.bool,
|
||||
accounts: PropTypes.object.isRequired,
|
||||
contacts: PropTypes.object.isRequired,
|
||||
contracts: PropTypes.object.isRequired,
|
||||
error: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
hint: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
tokens: PropTypes.object,
|
||||
onChange: PropTypes.func
|
||||
};
|
||||
|
||||
state = {
|
||||
editing: this.props.editing || false,
|
||||
entries: []
|
||||
}
|
||||
|
||||
render () {
|
||||
const { editing } = this.state;
|
||||
|
||||
return (
|
||||
<div className={ styles.inputselect }>
|
||||
{ editing ? this.renderInput() : this.renderSelect() }
|
||||
<Toggle
|
||||
className={ styles.toggle }
|
||||
label='Edit'
|
||||
labelPosition='right'
|
||||
toggled={ editing }
|
||||
onToggle={ this.onToggle } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderInput () {
|
||||
const { disabled, error, hint, label, value, tokens } = this.props;
|
||||
|
||||
return (
|
||||
<InputAddress
|
||||
disabled={ disabled }
|
||||
error={ error }
|
||||
hint={ hint }
|
||||
label={ label }
|
||||
value={ value }
|
||||
tokens={ tokens }
|
||||
onChange={ this.onChangeInput } />
|
||||
);
|
||||
}
|
||||
|
||||
renderSelect () {
|
||||
const { accounts, contacts, disabled, error, hint, label, value, tokens } = this.props;
|
||||
const { accounts, contacts, contracts, label, hint, error, value, onChange } = this.props;
|
||||
|
||||
return (
|
||||
<AddressSelect
|
||||
allowInput
|
||||
accounts={ accounts }
|
||||
contacts={ contacts }
|
||||
disabled={ disabled }
|
||||
contracts={ contracts }
|
||||
error={ error }
|
||||
label={ label }
|
||||
hint={ hint }
|
||||
error={ error }
|
||||
value={ value }
|
||||
tokens={ tokens }
|
||||
onChange={ this.onChangeSelect } />
|
||||
onChange={ onChange } />
|
||||
);
|
||||
}
|
||||
|
||||
onToggle = () => {
|
||||
const { editing } = this.state;
|
||||
|
||||
this.setState({
|
||||
editing: !editing
|
||||
});
|
||||
}
|
||||
|
||||
onChangeInput = (event, value) => {
|
||||
this.props.onChange(event, value);
|
||||
}
|
||||
|
||||
onChangeSelect = (event, value) => {
|
||||
this.props.onChange(event, value);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { accounts, contacts } = state.personal;
|
||||
const { accounts, contacts, contracts } = state.personal;
|
||||
|
||||
return {
|
||||
accounts,
|
||||
contacts
|
||||
contacts,
|
||||
contracts
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
17
js/src/ui/Form/InputChip/index.js
Normal file
17
js/src/ui/Form/InputChip/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 './inputChip';
|
||||
26
js/src/ui/Form/InputChip/inputChip.css
Normal file
26
js/src/ui/Form/InputChip/inputChip.css
Normal file
@@ -0,0 +1,26 @@
|
||||
/* Copyright 2015, 2016 Ethcore (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/>.
|
||||
*/
|
||||
|
||||
.chip {
|
||||
& > svg {
|
||||
width: 1rem !important;
|
||||
height: 1rem !important;
|
||||
margin: initial !important;
|
||||
margin-right: 4px !important;
|
||||
padding: 2px 0 !important;
|
||||
}
|
||||
}
|
||||
198
js/src/ui/Form/InputChip/inputChip.js
Normal file
198
js/src/ui/Form/InputChip/inputChip.js
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright 2015, 2016 Ethcore (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, { Component, PropTypes } from 'react';
|
||||
import { Chip } from 'material-ui';
|
||||
import ChipInput from 'material-ui-chip-input';
|
||||
import { blue300 } from 'material-ui/styles/colors';
|
||||
import { uniq } from 'lodash';
|
||||
|
||||
import styles from './inputChip.css';
|
||||
|
||||
export default class InputChip extends Component {
|
||||
static propTypes = {
|
||||
tokens: PropTypes.array.isRequired,
|
||||
className: PropTypes.string,
|
||||
hint: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
onTokensChange: PropTypes.func,
|
||||
onInputChange: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
addOnBlur: PropTypes.bool,
|
||||
clearOnBlur: PropTypes.bool
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
clearOnBlur: false,
|
||||
addOnBlur: false
|
||||
}
|
||||
|
||||
state = {
|
||||
focused: false
|
||||
}
|
||||
|
||||
render () {
|
||||
const { clearOnBlur, className, hint, label, tokens } = this.props;
|
||||
const { focused } = this.state;
|
||||
|
||||
const classes = `${className}`;
|
||||
|
||||
const textFieldStyle = {
|
||||
height: 55
|
||||
};
|
||||
|
||||
if (!focused) {
|
||||
textFieldStyle.width = 0;
|
||||
}
|
||||
|
||||
return (
|
||||
<ChipInput
|
||||
className={ classes }
|
||||
ref='chipInput'
|
||||
|
||||
value={ tokens }
|
||||
clearOnBlur={ clearOnBlur }
|
||||
floatingLabelText={ label }
|
||||
hintText={ hint }
|
||||
|
||||
chipRenderer={ this.chipRenderer }
|
||||
|
||||
onFocus={ this.handleFocus }
|
||||
onBlur={ this.handleBlur }
|
||||
onRequestAdd={ this.handleTokenAdd }
|
||||
onRequestDelete={ this.handleTokenDelete }
|
||||
onUpdateInput={ this.handleInputChange }
|
||||
|
||||
floatingLabelFixed
|
||||
fullWidth
|
||||
|
||||
hintStyle={ {
|
||||
bottom: 13,
|
||||
left: 0,
|
||||
transition: 'none'
|
||||
} }
|
||||
inputStyle={ {
|
||||
marginBottom: 18,
|
||||
width: 'initial'
|
||||
} }
|
||||
textFieldStyle={ textFieldStyle }
|
||||
underlineStyle={ {
|
||||
borderWidth: 2
|
||||
} }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
chipRenderer = (state, key) => {
|
||||
const { value, isFocused, isDisabled, handleClick, handleRequestDelete } = state;
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={ key }
|
||||
className={ styles.chip }
|
||||
style={ {
|
||||
margin: '15px 8px 0 0',
|
||||
float: 'left',
|
||||
pointerEvents: isDisabled ? 'none' : undefined,
|
||||
alignItems: 'center'
|
||||
} }
|
||||
labelStyle={ {
|
||||
paddingRight: 6,
|
||||
fontSize: '0.9rem',
|
||||
lineHeight: 'initial'
|
||||
} }
|
||||
backgroundColor={ isFocused ? blue300 : 'rgba(50, 50, 50, 0.73)' }
|
||||
onTouchTap={ handleClick }
|
||||
onRequestDelete={ handleRequestDelete }
|
||||
>
|
||||
{ value }
|
||||
</Chip>
|
||||
);
|
||||
}
|
||||
|
||||
handleFocus = () => {
|
||||
this.setState({ focused: true });
|
||||
}
|
||||
|
||||
handleBlur = () => {
|
||||
const { onBlur, addOnBlur } = this.props;
|
||||
|
||||
this.setState({ focused: false });
|
||||
|
||||
if (addOnBlur) {
|
||||
const { inputValue } = this.refs.chipInput.state;
|
||||
this.handleTokenAdd(inputValue);
|
||||
}
|
||||
|
||||
if (typeof onBlur === 'function') {
|
||||
onBlur();
|
||||
}
|
||||
}
|
||||
|
||||
handleTokenAdd = (value) => {
|
||||
const { tokens, onInputChange } = this.props;
|
||||
|
||||
const newTokens = uniq([].concat(tokens, value));
|
||||
|
||||
this.handleTokensChange(newTokens);
|
||||
|
||||
if (value === this.refs.chipInput.state.inputValue) {
|
||||
if (typeof onInputChange === 'function') {
|
||||
onInputChange('');
|
||||
}
|
||||
this.refs.chipInput.setState({ inputValue: '' });
|
||||
}
|
||||
}
|
||||
|
||||
handleTokenDelete = (value) => {
|
||||
const { tokens } = this.props;
|
||||
|
||||
const newTokens = uniq([]
|
||||
.concat(tokens)
|
||||
.filter(v => v !== value));
|
||||
|
||||
this.handleTokensChange(newTokens);
|
||||
this.refs.chipInput.focus();
|
||||
}
|
||||
|
||||
handleInputChange = (value) => {
|
||||
const { onInputChange } = this.props;
|
||||
|
||||
const splitTokens = value.split(/[\s,;]/);
|
||||
|
||||
const inputValue = (splitTokens.length <= 1)
|
||||
? value
|
||||
: splitTokens.slice(-1)[0].trim();
|
||||
|
||||
this.refs.chipInput.setState({ inputValue });
|
||||
|
||||
if (splitTokens.length > 1) {
|
||||
const tokensToAdd = splitTokens.slice(0, -1);
|
||||
tokensToAdd.forEach(token => this.handleTokenAdd(token));
|
||||
}
|
||||
|
||||
if (typeof onInputChange === 'function') {
|
||||
onInputChange(inputValue);
|
||||
}
|
||||
}
|
||||
|
||||
handleTokensChange = (tokens) => {
|
||||
const { onTokensChange } = this.props;
|
||||
|
||||
onTokensChange(tokens.filter(token => token && token.length > 0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import FormWrap from './FormWrap';
|
||||
import Input from './Input';
|
||||
import InputAddress from './InputAddress';
|
||||
import InputAddressSelect from './InputAddressSelect';
|
||||
import InputChip from './InputChip';
|
||||
import InputInline from './InputInline';
|
||||
import Select from './Select';
|
||||
|
||||
@@ -29,6 +30,7 @@ export {
|
||||
Input,
|
||||
InputAddress,
|
||||
InputAddressSelect,
|
||||
InputChip,
|
||||
InputInline,
|
||||
Select
|
||||
};
|
||||
|
||||
@@ -59,10 +59,9 @@ class IdentityIcon extends Component {
|
||||
updateIcon (_address, images) {
|
||||
const { api } = this.context;
|
||||
const { button, inline, tiny } = this.props;
|
||||
const iconsrc = images[_address];
|
||||
|
||||
if (iconsrc) {
|
||||
this.setState({ iconsrc });
|
||||
if (images[_address]) {
|
||||
this.setState({ iconsrc: `${api.dappsUrl}${images[_address]}` });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,11 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import Contracts from '../../contracts';
|
||||
import IdentityIcon from '../IdentityIcon';
|
||||
import IdentityName from '../IdentityName';
|
||||
import { Input, InputAddress } from '../Form';
|
||||
|
||||
import { fetchBytecode, fetchMethod } from '../../redux/providers/blockchainActions';
|
||||
|
||||
import styles from './methodDecoding.css';
|
||||
|
||||
const CONTRACT_CREATE = '0x60606040';
|
||||
@@ -41,12 +40,7 @@ class MethodDecoding extends Component {
|
||||
address: PropTypes.string.isRequired,
|
||||
tokens: PropTypes.object,
|
||||
transaction: PropTypes.object,
|
||||
historic: PropTypes.bool,
|
||||
|
||||
fetchBytecode: PropTypes.func,
|
||||
fetchMethod: PropTypes.func,
|
||||
bytecodes: PropTypes.object,
|
||||
methods: PropTypes.object
|
||||
historic: PropTypes.bool
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -63,58 +57,7 @@ class MethodDecoding extends Component {
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
const { transaction } = this.props;
|
||||
this.lookup(transaction);
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.setMethod(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (newProps) {
|
||||
const { transaction } = this.props;
|
||||
this.setMethod(newProps);
|
||||
|
||||
if (newProps.transaction.hash !== transaction.hash) {
|
||||
this.lookup(transaction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setMethod (props) {
|
||||
const { bytecodes, methods } = props;
|
||||
const { contractAddress, methodSignature, methodParams } = this.state;
|
||||
|
||||
if (contractAddress && bytecodes[contractAddress]) {
|
||||
const bytecode = bytecodes[contractAddress];
|
||||
|
||||
if (bytecode && bytecode !== '0x') {
|
||||
this.setState({ isContract: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (methodSignature && methods[methodSignature]) {
|
||||
const method = methods[methodSignature];
|
||||
const { api } = this.context;
|
||||
|
||||
let methodInputs = null;
|
||||
let methodName = null;
|
||||
|
||||
if (method && method.length) {
|
||||
const abi = api.util.methodToAbi(method);
|
||||
|
||||
methodName = abi.name;
|
||||
methodInputs = api.util
|
||||
.decodeMethodInput(abi, methodParams)
|
||||
.map((value, index) => {
|
||||
const type = abi.inputs[index].type;
|
||||
|
||||
return { type, value };
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ method, methodName, methodInputs });
|
||||
}
|
||||
this.lookup();
|
||||
}
|
||||
|
||||
render () {
|
||||
@@ -139,7 +82,7 @@ class MethodDecoding extends Component {
|
||||
|
||||
return (
|
||||
<div className={ styles.gasDetails }>
|
||||
{ historic ? 'Used' : 'Will use' } <span className={ styles.highlight }>{ gas.toFormat(0) } gas ({ gasPrice.div(1000000).toFormat(0) }M/<small>ETH</small>)</span> for a total transaction cost of <span className={ styles.highlight }>{ this.renderEtherValue(gasValue) }</span>
|
||||
{ historic ? 'Provided' : 'Provides' } <span className={ styles.highlight }>{ gas.toFormat(0) } gas ({ gasPrice.div(1000000).toFormat(0) }M/<small>ETH</small>)</span> for a total transaction value of <span className={ styles.highlight }>{ this.renderEtherValue(gasValue) }</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -268,7 +211,8 @@ class MethodDecoding extends Component {
|
||||
default:
|
||||
return (
|
||||
<Input
|
||||
disabled
|
||||
readOnly
|
||||
allowCopy
|
||||
key={ index }
|
||||
className={ styles.input }
|
||||
value={ this.renderValue(input.value) }
|
||||
@@ -320,7 +264,9 @@ class MethodDecoding extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
lookup (transaction) {
|
||||
lookup () {
|
||||
const { transaction } = this.props;
|
||||
|
||||
if (!transaction) {
|
||||
return;
|
||||
}
|
||||
@@ -346,26 +292,51 @@ class MethodDecoding extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const { fetchBytecode, fetchMethod } = this.props;
|
||||
Promise
|
||||
.all([
|
||||
api.eth.getCode(contractAddress),
|
||||
Contracts.get().signatureReg.lookup(signature)
|
||||
])
|
||||
.then(([bytecode, method]) => {
|
||||
let methodInputs = null;
|
||||
let methodName = null;
|
||||
|
||||
fetchBytecode(contractAddress);
|
||||
fetchMethod(signature);
|
||||
if (method && method.length) {
|
||||
const { methodParams } = this.state;
|
||||
const abi = api.util.methodToAbi(method);
|
||||
|
||||
methodName = abi.name;
|
||||
methodInputs = api.util
|
||||
.decodeMethodInput(abi, methodParams)
|
||||
.map((value, index) => {
|
||||
const type = abi.inputs[index].type;
|
||||
|
||||
return { type, value };
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
method,
|
||||
methodName,
|
||||
methodInputs,
|
||||
bytecode,
|
||||
isContract: bytecode && bytecode !== '0x'
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('lookup', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { tokens } = state.balances;
|
||||
const { bytecodes, methods } = state.blockchain;
|
||||
|
||||
return {
|
||||
tokens, bytecodes, methods
|
||||
};
|
||||
return { tokens };
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({
|
||||
fetchBytecode, fetchMethod
|
||||
}, dispatch);
|
||||
return bindActionCreators({}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
|
||||
@@ -22,16 +22,17 @@ import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
|
||||
const lightTheme = getMuiTheme(lightBaseTheme);
|
||||
const muiTheme = getMuiTheme(darkBaseTheme);
|
||||
|
||||
muiTheme.stepper.textColor = '#eee';
|
||||
muiTheme.stepper.disabledTextColor = '#777';
|
||||
muiTheme.inkBar.backgroundColor = 'transparent';
|
||||
muiTheme.paper.backgroundColor = 'rgba(0, 0, 0, 0.95)';
|
||||
muiTheme.raisedButton.primaryTextColor = 'white';
|
||||
muiTheme.snackbar.backgroundColor = 'rgba(255, 30, 30, 0.9)';
|
||||
muiTheme.snackbar.textColor = 'rgba(255, 255, 255, 0.75)';
|
||||
muiTheme.stepper.textColor = '#eee';
|
||||
muiTheme.stepper.disabledTextColor = '#777';
|
||||
muiTheme.tabs = lightTheme.tabs;
|
||||
muiTheme.tabs.backgroundColor = 'transparent';
|
||||
muiTheme.tabs.selectedTextColor = 'white';
|
||||
muiTheme.tabs.textColor = 'rgba(255, 255, 255, 0.5)'; // 'rgb(0, 151, 167)';
|
||||
muiTheme.tabs.textColor = 'rgba(255, 255, 255, 0.5)';
|
||||
muiTheme.textField.floatingLabelColor = 'rgba(255, 255, 255, 0.5)';
|
||||
muiTheme.textField.hintColor = 'rgba(255, 255, 255, 0.5)';
|
||||
muiTheme.textField.disabledTextColor = muiTheme.textField.textColor;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: transparent;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
z-index: 499;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ import { nextTooltip } from './actions';
|
||||
import styles from './tooltips.css';
|
||||
|
||||
class Tooltips extends Component {
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
currentId: PropTypes.number,
|
||||
closed: PropTypes.bool,
|
||||
@@ -33,6 +37,22 @@ class Tooltips extends Component {
|
||||
const { onNextTooltip } = this.props;
|
||||
|
||||
onNextTooltip();
|
||||
this.redirect();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.currentId !== this.props.currentId) {
|
||||
this.redirect(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
redirect (props = this.props) {
|
||||
const { currentId } = props;
|
||||
|
||||
if (currentId !== undefined && currentId !== -1) {
|
||||
const viewLink = '/accounts/';
|
||||
this.context.router.push(viewLink);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
.hash {
|
||||
padding-top: 1em;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.confirm {
|
||||
@@ -31,11 +32,13 @@
|
||||
}
|
||||
|
||||
.progressbar {
|
||||
margin: 0.5em !important;
|
||||
margin: 0.5em 0 !important;
|
||||
width: 30% !important;
|
||||
min-width: 220px;
|
||||
display: inline-block !important;
|
||||
height: 0.75em !important;
|
||||
}
|
||||
|
||||
.progressinfo {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { LinearProgress } from 'material-ui';
|
||||
import { txLink } from '../../3rdparty/etherscan/links';
|
||||
|
||||
import styles from './txHash.css';
|
||||
|
||||
@@ -29,7 +30,8 @@ class TxHash extends Component {
|
||||
|
||||
static propTypes = {
|
||||
hash: PropTypes.string.isRequired,
|
||||
isTest: PropTypes.bool
|
||||
isTest: PropTypes.bool,
|
||||
summary: PropTypes.bool
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -54,16 +56,22 @@ class TxHash extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { hash, isTest } = this.props;
|
||||
const link = `https://${isTest ? 'testnet.' : ''}etherscan.io/tx/${hash}`;
|
||||
const { hash, isTest, summary } = this.props;
|
||||
let header = null;
|
||||
|
||||
return (
|
||||
<div className={ styles.details }>
|
||||
if (!summary) {
|
||||
header = (
|
||||
<div className={ styles.header }>
|
||||
The transaction has been posted to the network with a transaction hash of
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.details }>
|
||||
{ header }
|
||||
<div className={ styles.hash }>
|
||||
<a href={ link } target='_blank'>{ hash }</a>
|
||||
<a href={ txLink(hash, isTest) } target='_blank'>{ hash }</a>
|
||||
</div>
|
||||
{ this.renderConfirmations() }
|
||||
</div>
|
||||
|
||||
@@ -16,16 +16,20 @@
|
||||
|
||||
import Actionbar from './Actionbar';
|
||||
import ActionbarExport from './Actionbar/Export';
|
||||
import ActionbarImport from './Actionbar/Import';
|
||||
import ActionbarSearch from './Actionbar/Search';
|
||||
import ActionbarSort from './Actionbar/Sort';
|
||||
import Badge from './Badge';
|
||||
import Balance from './Balance';
|
||||
import BlockStatus from './BlockStatus';
|
||||
import Button from './Button';
|
||||
import ConfirmDialog from './ConfirmDialog';
|
||||
import Container, { Title as ContainerTitle } from './Container';
|
||||
import ContextProvider from './ContextProvider';
|
||||
import CopyToClipboard from './CopyToClipboard';
|
||||
import Editor from './Editor';
|
||||
import Errors from './Errors';
|
||||
import Form, { AddressSelect, FormWrap, Input, InputAddress, InputAddressSelect, InputInline, Select } from './Form';
|
||||
import Form, { AddressSelect, FormWrap, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select } from './Form';
|
||||
import IdentityIcon from './IdentityIcon';
|
||||
import IdentityName from './IdentityName';
|
||||
import MethodDecoding from './MethodDecoding';
|
||||
@@ -41,22 +45,27 @@ import TxHash from './TxHash';
|
||||
export {
|
||||
Actionbar,
|
||||
ActionbarExport,
|
||||
ActionbarImport,
|
||||
ActionbarSearch,
|
||||
ActionbarSort,
|
||||
AddressSelect,
|
||||
Badge,
|
||||
Balance,
|
||||
BlockStatus,
|
||||
Button,
|
||||
ConfirmDialog,
|
||||
Container,
|
||||
ContainerTitle,
|
||||
ContextProvider,
|
||||
CopyToClipboard,
|
||||
Editor,
|
||||
Errors,
|
||||
Form,
|
||||
FormWrap,
|
||||
Input,
|
||||
InputAddress,
|
||||
InputAddressSelect,
|
||||
InputChip,
|
||||
InputInline,
|
||||
Select,
|
||||
IdentityIcon,
|
||||
|
||||
Reference in New Issue
Block a user