governance 2.0
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"presets": [
|
||||
"react",
|
||||
"es2015",
|
||||
"stage-1"
|
||||
],
|
||||
"plugins": ["transform-decorators-legacy" /* should always be the first plugin! */]
|
||||
}
|
21
LICENSE
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Oracles Network
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
49
README.md
|
@ -1,4 +1,4 @@
|
|||
# Oracles network Governance Dapp
|
||||
# POA Network Governance Dapp
|
||||
|
||||
## Supported browsers
|
||||
|
||||
|
@ -6,61 +6,46 @@
|
|||
|
||||
## MetaMask plugin setup
|
||||
|
||||
* Connect to Oracles network in MetaMask plugin (See [Connect to Oracles network via MetaMask](https://github.com/oraclesorg/oracles-wiki/blob/master/MetaMask-connect.md#connect-to-oracles-network-via-metamask))
|
||||
* Connect to POA Network in MetaMask plugin (See [Connect to Oracles network via MetaMask](https://github.com/poanetwork/wiki/blob/master/MetaMask-connect.md#connect-to-poa-network-via-metamask))
|
||||
|
||||
* Import your voting key to MetaMask Plugin (See [Governance section from wiki](https://github.com/oraclesorg/oracles-wiki/blob/master/governance.md)).
|
||||
* Import your voting key to MetaMask Plugin (See [Governance section from wiki](https://github.com/poanetwork/wiki/blob/master/governance.md)).
|
||||
|
||||
## Governance Dapp lifecycle
|
||||
|
||||
Check [Governance Dapp section from wiki](https://github.com/oraclesorg/oracles-wiki/blob/master/governance.md)
|
||||
Check [Governance Dapp section from wiki](https://github.com/poanetwork/wiki/blob/master/governance.md)
|
||||
|
||||
## Ballots page
|
||||
You'll see the page with the list of all ballots. Here you can switch to see only your **UNANSWERED** or **EXPIRED** ballots.
|
||||
You'll see the page with the list of all ballots. Here you can switch to see **ACTIVE** ballots.
|
||||
**Search** by ballots' data is available too.
|
||||
|
||||
Single ballot page is opened by clicking **VOTE NOW** button.
|
||||
|
||||
![](./docs/ballots.png)
|
||||
|
||||
## Single ballot page
|
||||
Here you can vote for or against notary. If total number of votes > 3, notary will be added or deleted from the network depending on votes majority after voting will be finished.
|
||||
|
||||
![](./docs/ballot.png)
|
||||
|
||||
## New ballot page
|
||||
Click **NEW BALLOT** button from any page to create a new ballot.
|
||||
|
||||
![](./docs/new_ballot_1.png)
|
||||
![](./docs/new_ballot.png)
|
||||
|
||||
![](./docs/new_ballot_2.png)
|
||||
|
||||
## Settings page
|
||||
You can return to this page from any page by clicking **Settings** button. You can choose your voting key here.
|
||||
|
||||
![](./docs/settings.png)
|
||||
|
||||
## Configuration file
|
||||
It is configured with [Oracles network contract](https://github.com/oraclesorg/oracles-contract)
|
||||
|
||||
Path: `./assets/javascripts/config.json`
|
||||
Path: `./src/contracts/addresses.js`
|
||||
|
||||
```
|
||||
{
|
||||
"environment": "live",
|
||||
"Ethereum": {
|
||||
"live": {
|
||||
"contractAddress": "Oracles_contract_address"
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
KEYS_MANAGER_ADDRESS: '0xfc90125492e58dbfe80c0bfb6a2a759c4f703ca8',
|
||||
VOTING_TO_CHANGE_KEYS_ADDRESS: '0x49df4ec19243263e5db22da5865b4f482b8323a0',
|
||||
VOTING_TO_CHANGE_MIN_THRESHOLD: '0x8829ebe113535826e8af17ed51f83755f675789a',
|
||||
VOTING_TO_CHANGE_PROXY: '0x6b728399b41a38d4109f7af2213d4cc31ca87812',
|
||||
METADATA_ADDRESS: '0xcBB2912666c7e8023B7ec78B6842702eB26336aC',
|
||||
POA_ADDRESS: '0x8bf38d4764929064f2d4d3a56520a76ab3df415b',
|
||||
BALLOTS_STORAGE_ADDRESS: '0x0d7590c7aedf1e7e85fc9a1ee88f6f17d3ba762f'
|
||||
}
|
||||
```
|
||||
|
||||
## Building from source
|
||||
|
||||
1) `npm install`
|
||||
1) `npm i`
|
||||
|
||||
2) `npm run sass`
|
||||
|
||||
3) `npm run coffee`
|
||||
|
||||
4) `npm start`
|
||||
2) `npm start`
|
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 541 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB |
|
@ -1,47 +0,0 @@
|
|||
function addBallot(web3, func, ballotViewObj, address, contractAddr, cb) {
|
||||
console.log(ballotViewObj);
|
||||
var funcParamsNumber = 7;
|
||||
var standardLength = 32;
|
||||
|
||||
SHA3Encrypt(web3, func, function(funcEncode) {
|
||||
var funcEncodePart = funcEncode.substring(0,10);
|
||||
var parameterLocation = standardLength * funcParamsNumber;
|
||||
|
||||
if (ballotViewObj.miningKey.indexOf("0x") > -1)
|
||||
ballotViewObj.miningKey = ballotViewObj.miningKey.substr(2);
|
||||
ballotViewObj.miningKey = ballotViewObj.miningKey.toLowerCase();
|
||||
|
||||
if (ballotViewObj.owner.indexOf("0x") > -1)
|
||||
ballotViewObj.owner = ballotViewObj.owner.substr(2);
|
||||
ballotViewObj.owner = ballotViewObj.owner.toLowerCase();
|
||||
|
||||
if (ballotViewObj.affectedKey.indexOf("0x") > -1)
|
||||
ballotViewObj.affectedKey = ballotViewObj.affectedKey.substr(2);
|
||||
ballotViewObj.affectedKey = ballotViewObj.affectedKey.toLowerCase();
|
||||
|
||||
ballotViewObj.addAction = JSON.parse(ballotViewObj.addAction);
|
||||
|
||||
var memoHex = "0x" + toUnifiedLengthRight(toHexString(toUTF8Array(ballotViewObj.memo)));
|
||||
|
||||
var data = funcEncodePart
|
||||
+ toUnifiedLengthLeft(ballotViewObj.ballotID.toString(16))
|
||||
+ toUnifiedLengthLeft(ballotViewObj.owner)
|
||||
+ toUnifiedLengthLeft(ballotViewObj.miningKey)
|
||||
+ toUnifiedLengthLeft(ballotViewObj.affectedKey)
|
||||
+ toUnifiedLengthLeft(ballotViewObj.affectedKeyType.toString(16))
|
||||
+ toUnifiedLengthLeft((+ballotViewObj.addAction).toString())
|
||||
+ toUnifiedLengthLeft(parameterLocation.toString(16))
|
||||
+ toUnifiedLengthLeft(bytesCount(ballotViewObj.memo).toString(16)) + memoHex.substring(2);
|
||||
|
||||
estimateGas(web3, address, contractAddr, data, function(estimatedGas, err) {
|
||||
console.log(estimatedGas);
|
||||
if (err) return cb(null, err);
|
||||
|
||||
estimatedGas += 100000;
|
||||
sendTx(web3, address, contractAddr, data, estimatedGas, function(txHash, err) {
|
||||
if (err) return cb(txHash, err);
|
||||
cb(txHash);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
function addValidator(web3, func, validatorViewObj, address, contractAddr, cb) {
|
||||
var funcParamsNumber = 7;
|
||||
var standardLength = 32;
|
||||
|
||||
SHA3Encrypt(web3, func, function(funcEncode) {
|
||||
var funcEncodePart = funcEncode.substring(0,10);
|
||||
if (validatorViewObj.miningKey.indexOf("0x") > -1)
|
||||
validatorViewObj.miningKey = validatorViewObj.miningKey.substr(2);
|
||||
|
||||
validatorViewObj.miningKey = validatorViewObj.miningKey.toLowerCase();
|
||||
|
||||
var fullNameHex = "0x" + toUnifiedLengthRight(toHexString(toUTF8Array(validatorViewObj.fullName)));
|
||||
var streetNameHex = "0x" + toUnifiedLengthRight(toHexString(toUTF8Array(validatorViewObj.streetName)));
|
||||
var stateHex = "0x" + toUnifiedLengthRight(toHexString(toUTF8Array(validatorViewObj.state)));
|
||||
|
||||
var parameterLocation1 = standardLength * funcParamsNumber;
|
||||
var parameterLocation2 = parameterLocation1 + standardLength*(countRows(fullNameHex));
|
||||
var parameterLocation3 = parameterLocation2 + standardLength*(countRows(streetNameHex));
|
||||
|
||||
var data = funcEncodePart
|
||||
+ toUnifiedLengthLeft(validatorViewObj.miningKey)
|
||||
+ toUnifiedLengthLeft(validatorViewObj.zip.toString(16))
|
||||
+ toUnifiedLengthLeft(validatorViewObj.licenseID.toString(16))
|
||||
+ toUnifiedLengthLeft(validatorViewObj.licenseExpiredAt.toString(16))
|
||||
+ toUnifiedLengthLeft(parameterLocation1.toString(16))
|
||||
+ toUnifiedLengthLeft(parameterLocation2.toString(16))
|
||||
+ toUnifiedLengthLeft(parameterLocation3.toString(16))
|
||||
+ toUnifiedLengthLeft(bytesCount(validatorViewObj.fullName).toString(16)) + fullNameHex.substring(2)
|
||||
+ toUnifiedLengthLeft(bytesCount(validatorViewObj.streetName).toString(16)) + streetNameHex.substring(2)
|
||||
+ toUnifiedLengthLeft(bytesCount(validatorViewObj.state).toString(16)) + stateHex.substring(2);
|
||||
|
||||
estimateGas(web3, address, contractAddr, data, function(estimatedGas, err) {
|
||||
if (err) return cb(null, err);
|
||||
|
||||
estimatedGas += 100000;
|
||||
sendTx(web3, address, contractAddr, data, estimatedGas, function(txHash, err) {
|
||||
if (err) return cb(txHash, err);
|
||||
cb(txHash);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
function showAlert(err, msg) {
|
||||
if (!err) {
|
||||
swal({
|
||||
title: "Error",
|
||||
text: msg,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (err.type != "REQUEST_REJECTED") {
|
||||
swal({
|
||||
title: "Error",
|
||||
text: msg,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
function generateBallotID() {
|
||||
var min = 10000000;
|
||||
var max = 99999999;
|
||||
return Math.floor(Math.random() * (max - min)) + min;
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
function SHA3Encrypt(web3, str, cb) {
|
||||
var strEncode = web3.utils.sha3(str);
|
||||
cb(strEncode);
|
||||
}
|
||||
|
||||
function estimateGas(web3, acc, contractAddr, data, cb) {
|
||||
web3.eth.estimateGas({
|
||||
from: acc,
|
||||
data: data,
|
||||
to: contractAddr
|
||||
}, function(err, estimatedGas) {
|
||||
if (err) console.log(err);
|
||||
console.log(estimatedGas);
|
||||
cb(estimatedGas, err);
|
||||
});
|
||||
}
|
||||
|
||||
function sendTx(web3, acc, contractAddr, data, estimatedGas, cb) {
|
||||
web3.eth.sendTransaction({
|
||||
from: acc,
|
||||
data: data,
|
||||
to: contractAddr,
|
||||
gas: estimatedGas
|
||||
}, function(err, txHash) {
|
||||
if (err) console.log(err);
|
||||
cb(txHash, err);
|
||||
});
|
||||
}
|
||||
|
||||
function call(web3, acc, contractAddr, data, cb) {
|
||||
var props;
|
||||
if (acc) props = { from: acc, data: data, to: contractAddr };
|
||||
else props = { data: data, to: contractAddr };
|
||||
|
||||
web3.eth.call(props, function(err, data) {
|
||||
if (err) console.log(err);
|
||||
cb(data);
|
||||
});
|
||||
}
|
||||
|
||||
function getTxCallBack(txHash, cb) {
|
||||
web3.eth.getTransaction(txHash, function(err, txDetails) {
|
||||
if (err) console.log(err);
|
||||
|
||||
if (!txDetails.blockNumber) {
|
||||
setTimeout(function() {
|
||||
getTxCallBack(txHash, cb);
|
||||
}, 2000)
|
||||
} else cb();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function getContractStringDataFromAddressKey(web3, acc, func, inputVal, i, contractAddr, cb) {
|
||||
var funcParamsNumber = 1;
|
||||
var standardLength = 32;
|
||||
|
||||
var parameterLocation = standardLength * funcParamsNumber;
|
||||
|
||||
SHA3Encrypt(web3, func, function(funcEncode) {
|
||||
var funcEncodePart = funcEncode.substring(0,10);
|
||||
|
||||
var data = funcEncodePart
|
||||
+ toUnifiedLengthLeft(inputVal);
|
||||
|
||||
call(web3, acc, contractAddr, data, function(respHex) {
|
||||
cb(i, hex2a(respHex));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getContractIntDataFromAddressKey(web3, acc, func, inputVal, i, contractAddr, cb) {
|
||||
var funcParamsNumber = 1;
|
||||
var standardLength = 32;
|
||||
|
||||
var parameterLocation = standardLength * funcParamsNumber;
|
||||
|
||||
SHA3Encrypt(web3, func, function(funcEncode) {
|
||||
var funcEncodePart = funcEncode.substring(0,10);
|
||||
|
||||
var data = funcEncodePart
|
||||
+ toUnifiedLengthLeft(inputVal);
|
||||
|
||||
call(web3, acc, contractAddr, data, function(respHex) {
|
||||
cb(i, parseInt(respHex, 16));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getContractAddressDataFromAddressKey(web3, acc, func, inputVal, i, contractAddr, cb) {
|
||||
var funcParamsNumber = 1;
|
||||
var standardLength = 32;
|
||||
|
||||
var parameterLocation = standardLength * funcParamsNumber;
|
||||
|
||||
SHA3Encrypt(web3, func, function(funcEncode) {
|
||||
var funcEncodePart = funcEncode.substring(0,10);
|
||||
|
||||
var data = funcEncodePart
|
||||
+ toUnifiedLengthLeft(inputVal);
|
||||
|
||||
call(web3, acc, contractAddr, data, function(respHex) {
|
||||
cb(i, respHex);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
//check current network page is connected to. Alerts, if not Oracles network
|
||||
async function checkNetworkVersion(web3, cb) {
|
||||
var msgNotOracles = "You aren't connected to Oracles network. Please, switch on Oracles plugin and choose Oracles network. Check Oracles network <a href='https://github.com/oraclesorg/oracles-wiki' target='blank'>wiki</a> for more info.";
|
||||
let config = await getConfig()
|
||||
web3.eth.net.getId().then(function(connectedNetworkID) {
|
||||
console.log("connectedNetworkID: " + connectedNetworkID);
|
||||
connectedNetworkID = parseInt(connectedNetworkID);
|
||||
switch (connectedNetworkID) {
|
||||
case 1: {
|
||||
console.log('This is mainnet');
|
||||
swal("Warning", msgNotOracles, "warning");
|
||||
return false;
|
||||
} break;
|
||||
case 2: {
|
||||
console.log('This is the deprecated Morden test network.');
|
||||
swal("Warning", msgNotOracles, "warning");
|
||||
return false;
|
||||
} break;
|
||||
case 3: {
|
||||
console.log('This is the ropsten test network.');
|
||||
swal("Warning", msgNotOracles, "warning");
|
||||
return false;
|
||||
} break;
|
||||
case config.networkID: {
|
||||
console.log('This is Oracles from Metamask');
|
||||
return true;
|
||||
} break;
|
||||
default: {
|
||||
console.log('This is an unknown network.');
|
||||
swal("Warning", msgNotOracles, "warning");
|
||||
return false;
|
||||
} break;
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
function checkVotingKey(web3, func, key, contractAddr, cb) {
|
||||
var funcParamsNumber = 1;
|
||||
var standardLength = 32;
|
||||
|
||||
SHA3Encrypt(web3, func, function(funcEncode) {
|
||||
var funcEncodePart = funcEncode.substring(0,10);
|
||||
|
||||
var data = funcEncodePart
|
||||
+ toUnifiedLengthLeft(key.substr(2));
|
||||
|
||||
call(web3, key, contractAddr, data, function(respHex) {
|
||||
console.log(respHex);
|
||||
cb(parseInt(respHex, 16));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
function hex2a(hexx) {
|
||||
var hex = hexx.toString();//force conversion
|
||||
var str = '';
|
||||
for (var i = 0; i < hex.length; i += 2) {
|
||||
var code = parseInt(hex.substr(i, 2), 16);
|
||||
if (code != 0 && !isNaN(code)) {
|
||||
str += String.fromCharCode(code);
|
||||
}
|
||||
}
|
||||
str = str.substr(2);
|
||||
return str;
|
||||
}
|
||||
|
||||
function toUnifiedLengthLeft(strIn) {//for numbers
|
||||
var strOut = "";
|
||||
for (var i = 0; i < 64 - strIn.length; i++) {
|
||||
strOut += "0"
|
||||
}
|
||||
strOut += strIn;
|
||||
return strOut;
|
||||
}
|
||||
|
||||
function countRows(strIn) {
|
||||
var rowsCount = 0;
|
||||
if (strIn.length%64 > 0)
|
||||
rowsCount = parseInt(strIn.length/64) + 1;
|
||||
else
|
||||
rowsCount = parseInt(strIn.length/64);
|
||||
return rowsCount;
|
||||
}
|
||||
|
||||
function toUnifiedLengthRight(strIn) {//for strings
|
||||
var strOut = "";
|
||||
strOut += strIn;
|
||||
var rowsCount = countRows(strIn);
|
||||
for (var i = 0; i < rowsCount*64 - strIn.length; i++) {
|
||||
strOut += "0"
|
||||
}
|
||||
return strOut;
|
||||
}
|
||||
|
||||
String.prototype.hexEncode = function(){
|
||||
var hex, i;
|
||||
|
||||
var result = "";
|
||||
for (i=0; i<this.length; i++) {
|
||||
hex = this.charCodeAt(i).toString(16);
|
||||
result += hex.slice(-4);
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function toUTF8Array(str) {
|
||||
var utf8 = [];
|
||||
for (var i=0; i < str.length; i++) {
|
||||
var charcode = str.charCodeAt(i);
|
||||
if (charcode < 0x80) utf8.push(charcode);
|
||||
else if (charcode < 0x800) {
|
||||
utf8.push(0xc0 | (charcode >> 6),
|
||||
0x80 | (charcode & 0x3f));
|
||||
}
|
||||
else if (charcode < 0xd800 || charcode >= 0xe000) {
|
||||
utf8.push(0xe0 | (charcode >> 12),
|
||||
0x80 | ((charcode>>6) & 0x3f),
|
||||
0x80 | (charcode & 0x3f));
|
||||
}
|
||||
// surrogate pair
|
||||
else {
|
||||
i++;
|
||||
// UTF-16 encodes 0x10000-0x10FFFF by
|
||||
// subtracting 0x10000 and splitting the
|
||||
// 20 bits of 0x0-0xFFFFF into two halves
|
||||
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
|
||||
| (str.charCodeAt(i) & 0x3ff));
|
||||
utf8.push(0xf0 | (charcode >>18),
|
||||
0x80 | ((charcode>>12) & 0x3f),
|
||||
0x80 | ((charcode>>6) & 0x3f),
|
||||
0x80 | (charcode & 0x3f));
|
||||
}
|
||||
}
|
||||
return utf8;
|
||||
}
|
||||
|
||||
function toHexString(byteArray) {
|
||||
return byteArray.map(function(byte) {
|
||||
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
|
||||
}).join('')
|
||||
}
|
||||
|
||||
function bytesCount(s) {
|
||||
return encodeURI(s).split(/%..|./).length - 1;
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
function createCookie(name, value, days) {
|
||||
var expires;
|
||||
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toGMTString();
|
||||
} else {
|
||||
expires = "";
|
||||
}
|
||||
document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
|
||||
}
|
||||
|
||||
function readCookie(name) {
|
||||
var nameEQ = encodeURIComponent(name) + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
||||
}
|
||||
return null;
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
function formatDate(date, format, utc) {
|
||||
//var MMMM = ["\x00", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||||
var MMMM = ["\x00", "января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"];
|
||||
var MMM = ["\x01", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
var dddd = ["\x02", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
||||
var ddd = ["\x03", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
|
||||
function ii(i, len) {
|
||||
var s = i + "";
|
||||
len = len || 2;
|
||||
while (s.length < len) s = "0" + s;
|
||||
return s;
|
||||
}
|
||||
|
||||
var y = utc ? date.getUTCFullYear() : date.getFullYear();
|
||||
format = format.replace(/(^|[^\\])yyyy+/g, "$1" + y);
|
||||
format = format.replace(/(^|[^\\])yy/g, "$1" + y.toString().substr(2, 2));
|
||||
format = format.replace(/(^|[^\\])y/g, "$1" + y);
|
||||
|
||||
var M = (utc ? date.getUTCMonth() : date.getMonth()) + 1;
|
||||
format = format.replace(/(^|[^\\])MMMM+/g, "$1" + MMMM[0]);
|
||||
format = format.replace(/(^|[^\\])MMM/g, "$1" + MMM[0]);
|
||||
format = format.replace(/(^|[^\\])MM/g, "$1" + ii(M));
|
||||
format = format.replace(/(^|[^\\])M/g, "$1" + M);
|
||||
|
||||
var d = utc ? date.getUTCDate() : date.getDate();
|
||||
format = format.replace(/(^|[^\\])dddd+/g, "$1" + dddd[0]);
|
||||
format = format.replace(/(^|[^\\])ddd/g, "$1" + ddd[0]);
|
||||
format = format.replace(/(^|[^\\])dd/g, "$1" + ii(d));
|
||||
format = format.replace(/(^|[^\\])d/g, "$1" + d);
|
||||
|
||||
var H = utc ? date.getUTCHours() : date.getHours();
|
||||
format = format.replace(/(^|[^\\])HH+/g, "$1" + ii(H));
|
||||
format = format.replace(/(^|[^\\])H/g, "$1" + H);
|
||||
|
||||
var h = H > 12 ? H - 12 : H == 0 ? 12 : H;
|
||||
format = format.replace(/(^|[^\\])hh+/g, "$1" + ii(h));
|
||||
format = format.replace(/(^|[^\\])h/g, "$1" + h);
|
||||
|
||||
var m = utc ? date.getUTCMinutes() : date.getMinutes();
|
||||
format = format.replace(/(^|[^\\])mm+/g, "$1" + ii(m));
|
||||
format = format.replace(/(^|[^\\])m/g, "$1" + m);
|
||||
|
||||
var s = utc ? date.getUTCSeconds() : date.getSeconds();
|
||||
format = format.replace(/(^|[^\\])ss+/g, "$1" + ii(s));
|
||||
format = format.replace(/(^|[^\\])s/g, "$1" + s);
|
||||
|
||||
var f = utc ? date.getUTCMilliseconds() : date.getMilliseconds();
|
||||
format = format.replace(/(^|[^\\])fff+/g, "$1" + ii(f, 3));
|
||||
f = Math.round(f / 10);
|
||||
format = format.replace(/(^|[^\\])ff/g, "$1" + ii(f));
|
||||
f = Math.round(f / 10);
|
||||
format = format.replace(/(^|[^\\])f/g, "$1" + f);
|
||||
|
||||
var T = H < 12 ? "AM" : "PM";
|
||||
format = format.replace(/(^|[^\\])TT+/g, "$1" + T);
|
||||
format = format.replace(/(^|[^\\])T/g, "$1" + T.charAt(0));
|
||||
|
||||
var t = T.toLowerCase();
|
||||
format = format.replace(/(^|[^\\])tt+/g, "$1" + t);
|
||||
format = format.replace(/(^|[^\\])t/g, "$1" + t.charAt(0));
|
||||
|
||||
var tz = -date.getTimezoneOffset();
|
||||
var K = utc || !tz ? "Z" : tz > 0 ? "+" : "-";
|
||||
if (!utc) {
|
||||
tz = Math.abs(tz);
|
||||
var tzHrs = Math.floor(tz / 60);
|
||||
var tzMin = tz % 60;
|
||||
K += ii(tzHrs) + ":" + ii(tzMin);
|
||||
}
|
||||
format = format.replace(/(^|[^\\])K/g, "$1" + K);
|
||||
|
||||
var day = (utc ? date.getUTCDay() : date.getDay()) + 1;
|
||||
format = format.replace(new RegExp(dddd[0], "g"), dddd[day]);
|
||||
format = format.replace(new RegExp(ddd[0], "g"), ddd[day]);
|
||||
|
||||
format = format.replace(new RegExp(MMMM[0], "g"), MMMM[M]);
|
||||
format = format.replace(new RegExp(MMM[0], "g"), MMM[M]);
|
||||
|
||||
format = format.replace(/\\(.)/g, "$1");
|
||||
|
||||
return format;
|
||||
};
|
||||
|
||||
function getDateDiff(dateStart, dateEnd) {
|
||||
var periodInMinutes = Math.floor((dateEnd - dateStart)/60) + 1;
|
||||
if (periodInMinutes <= 0) {
|
||||
return "00:00";
|
||||
}
|
||||
|
||||
|
||||
var hours = Math.floor(periodInMinutes/60);
|
||||
var minutes = periodInMinutes%60;
|
||||
if (minutes == 60) {
|
||||
hours++;
|
||||
minutes = 0;
|
||||
}
|
||||
|
||||
var hoursStr = hours.toString();
|
||||
if (hours < 9)
|
||||
hoursStr = "0" + hours.toString();
|
||||
var minutesStr = minutes.toString();
|
||||
if (minutes < 9)
|
||||
minutesStr = "0" + minutes.toString();
|
||||
|
||||
return hoursStr + ":" + minutesStr;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
function filterBallots(searchInput) {
|
||||
return ballotsArrayFiltered.map(function(ballot, i) {
|
||||
if (ballot) {
|
||||
var searchValidated = validateSearch(ballot, searchInput.toLowerCase());
|
||||
if (!searchValidated) return null;
|
||||
else return ballot;
|
||||
} else return null;
|
||||
})
|
||||
}
|
||||
|
||||
function validateSearch(ballot, searchInput) {
|
||||
var ballotID = Object.keys(ballot)[0];
|
||||
|
||||
var ballotObj = ballot[ballotID];
|
||||
if (ballotObj["memo"].toLowerCase().indexOf(searchInput) > -1) return true;
|
||||
else if (ballotObj["fullName"].toLowerCase().indexOf(searchInput) > -1) return true;
|
||||
else if (ballotObj["address"].toLowerCase().indexOf(searchInput) > -1) return true;
|
||||
else if (ballotObj["state"].toLowerCase().indexOf(searchInput) > -1) return true;
|
||||
else if (ballotObj["zip"].toLowerCase().indexOf(searchInput) > -1) return true;
|
||||
else if (ballotObj["zip"].toLowerCase().indexOf(searchInput) > -1) return true;
|
||||
else if (ballotObj["licenseID"].toLowerCase().indexOf(searchInput) > -1) return true;
|
||||
else if (ballotObj["licenseExpiredAt"].toLowerCase().indexOf(searchInput) > -1) return true;
|
||||
else if (ballotObj["miningKey"].toString().indexOf(searchInput) > -1) return true;
|
||||
|
||||
return false;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
//get current account chosen in MetaMask or opened at Parity
|
||||
function getAccounts(cb) {
|
||||
web3.eth.getAccounts(function(err, accounts) {
|
||||
if (err) {
|
||||
$(".loading-container").hide();
|
||||
showAlert(err, err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
cb(accounts);
|
||||
});
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
function getBallots(web3, func, acc, contractAddress, cb) {
|
||||
SHA3Encrypt(web3, func, function(funcEncode) {
|
||||
var funcEncodePart = funcEncode.substring(0,10);
|
||||
|
||||
var data = funcEncodePart;
|
||||
|
||||
call(web3, null, contractAddress, data, function(ballotsResp) {
|
||||
ballotsResp = ballotsResp.substring(2, ballotsResp.length);
|
||||
var ballotsArray = [];
|
||||
var item = "";
|
||||
for (var i = 0; i < ballotsResp.length; i++) {
|
||||
item+=ballotsResp[i];
|
||||
if ((i + 1)%64 == 0) {
|
||||
item = item.substr(item.length - 40, 40);
|
||||
ballotsArray.push(item);
|
||||
item = "";
|
||||
}
|
||||
}
|
||||
ballotsArray.shift();
|
||||
ballotsArray.shift(); //number of elements
|
||||
|
||||
if (ballotsArray.length == 0) {
|
||||
cb(ballotsArray);
|
||||
return;
|
||||
}
|
||||
|
||||
var ballotsArrayOut = [];
|
||||
var iasync = [];
|
||||
var ballotDataCount = 12;
|
||||
for (var i = 0; i < ballotsArray.length; i++) {
|
||||
iasync.push(0);
|
||||
getBallotMemo(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("memo", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
ballotCreatedAt(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("createdAt", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
getBallotVotingStart(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("votingStart", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
getBallotVotingEnd(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("votingEnd", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
getVotesFor(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("votesFor", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
getVotesAgainst(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("votesAgainst", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
getBallotAction(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("action", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
ballotIsVoted(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("voted", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
getBallotMiningKey(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("miningKey", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
getBallotAffectedKey(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("affectedKey", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
getBallotAffectedKeyType(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("affectedKeyType", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
|
||||
getBallotOwner(web3, acc, ballotsArray[i], i, contractAddress, function(_i, resp) {
|
||||
iasync[_i]++;
|
||||
ballotsArrayOut = getBallotsPropertyCallback("owner", web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getBallotsPropertyCallback(prop, web3, contractAddress, resp, _i, iasync, ballotsArray, ballotDataCount, ballotsArrayOut, cb) {
|
||||
if (!ballotsArrayOut[_i]) {
|
||||
var ballot = {};
|
||||
ballot[ballotsArray[_i]] = {};
|
||||
ballot[ballotsArray[_i]][prop] = resp;
|
||||
ballotsArrayOut.push(ballot);
|
||||
} else ballotsArrayOut[_i][ballotsArray[_i]][prop] = resp;
|
||||
|
||||
var finish = true;
|
||||
for (var j = 0; j < iasync.length; j++) {
|
||||
if (iasync[j] < ballotDataCount) {
|
||||
finish = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (finish) {
|
||||
for (var j = 0; j < ballotsArray.length; j++) {
|
||||
var jasync = 0;
|
||||
var miningKey = ballotsArrayOut[j][ballotsArray[j]].miningKey;
|
||||
if (miningKey.length > 40) miningKey = miningKey.substr(miningKey.length - 40);
|
||||
}
|
||||
cb(ballotsArrayOut);
|
||||
return false;
|
||||
} else return ballotsArrayOut;
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
function getBallotMemo(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getBallotMemo(uint256)";
|
||||
getContractStringDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function ballotCreatedAt(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "ballotCreatedAt(uint256)";
|
||||
getContractIntDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getBallotVotingStart(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getBallotVotingStart(uint256)";
|
||||
getContractIntDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getBallotVotingEnd(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getBallotVotingEnd(uint256)";
|
||||
getContractIntDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getVotesFor(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getVotesFor(uint256)";
|
||||
getContractIntDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getVotesAgainst(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getVotesAgainst(uint256)";
|
||||
getContractIntDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getBallotAction(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getBallotAction(uint256)";
|
||||
getContractIntDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function ballotIsVoted(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "ballotIsVoted(uint256)";
|
||||
getContractIntDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getBallotMiningKey(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getBallotMiningKey(uint256)";
|
||||
getContractAddressDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getBallotAffectedKey(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getBallotAffectedKey(uint256)";
|
||||
getContractAddressDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getBallotAffectedKeyType(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getBallotAffectedKeyType(uint256)";
|
||||
getContractIntDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getBallotOwner(web3, acc, ballotID, i, contractAddr, cb) {
|
||||
var func = "getBallotOwner(uint256)";
|
||||
getContractStringDataFromAddressKey(web3, acc, func, ballotID, i, contractAddr, cb);
|
||||
}
|
||||
|
||||
function getBallotData(web3, acc, ballotID, contractAddress, cb) {
|
||||
var iasync = 0;
|
||||
var ballotDataCount = 12;
|
||||
var ballot = {};
|
||||
getBallotMemo(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("memo", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
ballotCreatedAt(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("createdAt", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
getBallotVotingStart(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("votingStart", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
getBallotVotingEnd(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("votingEnd", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
getVotesFor(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("votesFor", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
getVotesAgainst(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("votesAgainst", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
getBallotAction(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("action", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
ballotIsVoted(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("voted", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
getBallotMiningKey(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("miningKey", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
getBallotAffectedKey(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("affectedKey", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
getBallotAffectedKeyType(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("affectedKeyType", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
|
||||
getBallotOwner(web3, acc, ballotID, null, contractAddress, function(_i, resp) {
|
||||
iasync++;
|
||||
ballot = getBallotPropertyCallback("owner", web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb);
|
||||
});
|
||||
}
|
||||
|
||||
function getBallotPropertyCallback(prop, web3, contractAddress, ballotID, resp, iasync, ballot, ballotDataCount, cb) {
|
||||
if (Object.keys(ballot).length == 0) {
|
||||
ballot[ballotID] = {};
|
||||
ballot[ballotID][prop] = resp;
|
||||
} else ballot[ballotID][prop] = resp;
|
||||
|
||||
if (iasync == ballotDataCount) {
|
||||
cb(ballot[ballotID]);
|
||||
return false;
|
||||
} else return ballot;
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
function getBallotView(acc, ballotID, ballotPropsObj, isVotingEnabled, web3, contractAddress, cb) {
|
||||
if (ballotPropsObj) {
|
||||
return ballotViewObject(ballotID, ballotPropsObj, isVotingEnabled);
|
||||
} else {
|
||||
getBallotData(web3, acc, ballotID, contractAddress, function(ballotPropsObj) {
|
||||
cb(ballotViewObject(ballotID, ballotPropsObj, isVotingEnabled));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function ballotViewObject(ballotID, ballotPropsObj, isVotingEnabled) {
|
||||
//votes
|
||||
var votesFor = ballotPropsObj["votesFor"];
|
||||
var votesAgainst = ballotPropsObj["votesAgainst"];
|
||||
var votesTotal = ballotPropsObj["votesFor"] + ballotPropsObj["votesAgainst"];
|
||||
var votesForPerc = 0;
|
||||
var votesAgainstPerc = 0;
|
||||
if (votesTotal > 0) {
|
||||
votesForPerc = Math.round((votesFor / votesTotal) * 100, 0);
|
||||
votesAgainstPerc = Math.round((votesAgainst / votesTotal) * 100, 0);
|
||||
}
|
||||
//action
|
||||
var actionDN;
|
||||
switch(ballotPropsObj["action"]) {
|
||||
case 0:
|
||||
actionDN = "Remove Notary";
|
||||
break;
|
||||
case 1:
|
||||
actionDN = "Add new Notary";
|
||||
break;
|
||||
}
|
||||
//miningKey
|
||||
var miningKey = ballotPropsObj["miningKey"];
|
||||
if (miningKey.length > 40) miningKey = "0x" + miningKey.substr(miningKey.length - 40);
|
||||
//affectedKey
|
||||
var affectedKey = ballotPropsObj["affectedKey"];
|
||||
if (affectedKey.length > 40) affectedKey = "0x" + affectedKey.substr(affectedKey.length - 40);
|
||||
//affectedKeyType
|
||||
var affectedKeyType;
|
||||
switch(ballotPropsObj["affectedKeyType"]) {
|
||||
case 0:
|
||||
affectedKeyType = "mining key";
|
||||
break;
|
||||
case 1:
|
||||
affectedKeyType = "voting key";
|
||||
break;
|
||||
case 2:
|
||||
affectedKeyType = "payout key";
|
||||
break;
|
||||
}
|
||||
//time to start/end
|
||||
var timeToVotingStart = getDateDiff(Math.floor(Date.now() / 1000), parseInt(ballotPropsObj["votingStart"]));
|
||||
var timeToVotingEnd = getDateDiff(Math.floor(Date.now() / 1000), parseInt(ballotPropsObj["votingEnd"]));
|
||||
var timeToVotingStartEnd = timeToVotingStart;
|
||||
var timeToVotingStartEndLabel = "To start";
|
||||
if (timeToVotingStart == "00:00") {
|
||||
timeToVotingStartEnd = timeToVotingEnd;
|
||||
timeToVotingStartEndLabel = "To end"
|
||||
}
|
||||
|
||||
return `<div class="vote-i" ballot-id="` + ballotID + `">
|
||||
<div class="vote-header">
|
||||
<div class="vote-person left">
|
||||
<img src="./assets/images/person.png" alt="" class="vote-person-img">
|
||||
<p class="vote-person-name">` + (ballotPropsObj["owner"]?ballotPropsObj["owner"]:``) + `</p>
|
||||
<div class="vote-person-create">` + formatDate(new Date(parseInt(ballotPropsObj["createdAt"])*1000), "MM/dd/yyyy h:mm TT") + `</div>
|
||||
</div>
|
||||
<div class="vote-time right">
|
||||
<div class="vote-time-timer">` + timeToVotingStartEnd + `</div>
|
||||
<div class="vote-time-to">` + timeToVotingStartEndLabel + `</div>
|
||||
</div>
|
||||
` + (isVotingEnabled?``:`<a href="#" class="vote-now right" ballot-id="` + ballotID + `" ` + (timeToVotingEnd == "00:00"?`hidden`:``) + `>Vote now</a>`) + `
|
||||
</div>
|
||||
<div class="vote-body">
|
||||
<div class="vote-body-i">
|
||||
<p class="vote-body-title">
|
||||
Proposal
|
||||
<span class="vote-tooltip-container">
|
||||
<span class="vote-tooltip-icon"></span>
|
||||
<span class="vote-tooltip">
|
||||
<span class="vote-tooltip-text">
|
||||
<span class="vote-tooltip-title">How does it work?</span>
|
||||
<span class="vote-tooltip-description">
|
||||
If you are a validator in Oracles network you can sign a vote with your voting key.
|
||||
Please refer to voting FAQ on
|
||||
<a href="https://forum.oracles.org/">https://forum.oracles.org/</a>
|
||||
</span>
|
||||
</span>
|
||||
<span class="vote-tooltip-shadow"></span>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<p class="vote-body-description">
|
||||
` + ballotPropsObj["memo"] + `
|
||||
</p>
|
||||
</div>
|
||||
<div class="vote-body-i">
|
||||
<p class="vote-body-title">Mining key</p>
|
||||
<p class="vote-body-description">
|
||||
` + miningKey + `
|
||||
</p>
|
||||
<p class="vote-body-title-secondary">Affected key (` + affectedKeyType + `)</p>
|
||||
<p class="vote-body-description">
|
||||
` + affectedKey + `
|
||||
</p>
|
||||
</div>
|
||||
<div class="vote-body-i">
|
||||
<p class="vote-body-title">Title</p>
|
||||
<p class="vote-body-description">
|
||||
` + actionDN + `
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vote-rating">
|
||||
<div class="vote-rating-i left">
|
||||
<p class="vote-rating-value left">Yes</p>
|
||||
<div class="vote-rating-got right">
|
||||
<strong>` + votesForPerc + `%</strong>
|
||||
<p>Votes: ` + votesFor + `</p>
|
||||
</div>
|
||||
<div class="vote-rating-scale vote-rating-scale_yes">
|
||||
<div class="vote-rating-scale-active" style="width: ` + votesForPerc + `%"></div>
|
||||
</div>`
|
||||
+ (isVotingEnabled? `<a href="#" class="vote-button vote-rating-yes left">Vote</a>`:``) +
|
||||
`</div>
|
||||
<div class="vote-rating-i right">
|
||||
<p class="vote-rating-value left">No</p>
|
||||
<div class="vote-button vote-rating-got right">
|
||||
<strong>` + votesAgainstPerc + `%</strong>
|
||||
<p>Votes: ` + votesAgainst + `</p>
|
||||
</div>
|
||||
<div class="vote-rating-scale vote-rating-scale_no">
|
||||
<div class="vote-rating-scale-active" style="width: ` + votesAgainstPerc + `%"></div>
|
||||
</div>`
|
||||
+ (isVotingEnabled? `<a href="#" class="vote-button vote-rating-no right">Vote</a>`:``) +
|
||||
`</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
//gets config file with address of Oracles contract
|
||||
async function getConfig(cb) {
|
||||
let config = await $.getJSON("./assets/javascripts/config.json")
|
||||
let contractAddress = config.Ethereum[config.environment].contractAddress
|
||||
let abi = config.Ethereum[config.environment].abi
|
||||
let networkID = config.networkID
|
||||
let configJSON = {
|
||||
contractAddress,
|
||||
networkID,
|
||||
abi
|
||||
}
|
||||
if (cb) cb(configJSON)
|
||||
return configJSON;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
//gets web3 object from MetaMask or Parity
|
||||
function getWeb3(callback) {
|
||||
if (typeof window.web3 === 'undefined') {
|
||||
// no web3, use fallback
|
||||
console.error("Please use a web3 browser");
|
||||
var msgNotEthereum = "You aren't connected to Oracles Network. Please, switch on Oracles plugin and refresh the page. Check Oracles network <a href='https://github.com/oraclesorg/oracles-wiki' target='blank'>wiki</a> for more info.";
|
||||
swal("Warning", msgNotEthereum, "warning");
|
||||
callback(myWeb3, false);
|
||||
} else {
|
||||
// window.web3 == web3 most of the time. Don't override the provided,
|
||||
// web3, just wrap it in your Web3.
|
||||
var myWeb3 = new Web3(window.web3.currentProvider);
|
||||
|
||||
// the default account doesn't seem to be persisted, copy it to our
|
||||
// new instance
|
||||
myWeb3.eth.defaultAccount = window.web3.eth.defaultAccount;
|
||||
|
||||
let isOraclesNetwork = checkNetworkVersion(myWeb3)
|
||||
callback(myWeb3, isOraclesNetwork);
|
||||
}
|
||||
}
|
|
@ -1,375 +0,0 @@
|
|||
//launches main application
|
||||
function startDapp(web3, isOraclesNetwork) {
|
||||
$(function() {
|
||||
if (!isOraclesNetwork) {
|
||||
$(".loading-container").hide();
|
||||
return;
|
||||
}
|
||||
var ballotsArrayFiltered = [];
|
||||
var votingKey;
|
||||
|
||||
getAccounts(async function(accounts) {
|
||||
let config = await getConfig()
|
||||
getConfigCallBack(web3, accounts, config.contractAddress);
|
||||
});
|
||||
|
||||
//getting of config callback
|
||||
function getConfigCallBack(web3, accounts, contractAddress) {
|
||||
//checks if chosen account is valid voting key
|
||||
if (accounts.length == 1) {
|
||||
var possiblePayoutKey = accounts[0];
|
||||
checkVotingKey(web3,
|
||||
"checkVotingKeyValidity(address)",
|
||||
possiblePayoutKey,
|
||||
contractAddress,
|
||||
function(_isActive) {
|
||||
_isActive = !!+_isActive;
|
||||
if (!_isActive) {
|
||||
$(".loading-container").hide();
|
||||
swal("Warning", "Current key isn't valid voting key. Please, choose your voting key in MetaMask client and reload the page. Check Oracles network <a href='https://github.com/oraclesorg/oracles-wiki' target='blank'>wiki</a> for more info.", "warning");
|
||||
} else $(".choose-key-button").trigger("click");
|
||||
});
|
||||
} else if (accounts.length == 0) {
|
||||
$(".loading-container").hide();
|
||||
swal("Warning", "You haven't chosen any account in MetaMask. Please, choose your voting key in MetaMask client and reload the page. Check Oracles network <a href='https://github.com/oraclesorg/oracles-wiki' target='blank'>wiki</a> for more info.", "warning");
|
||||
}
|
||||
|
||||
$(".loading-container").hide();
|
||||
|
||||
//choose key button onclick event
|
||||
$(".choose-key-button").on("click", function() {
|
||||
$(".loading-container").show();
|
||||
ballotsNavPan();
|
||||
$(".key-content").addClass("hidden");
|
||||
$(".content").removeClass("hidden");
|
||||
$(".container.vote").empty();
|
||||
$(".container.new-ballot").addClass("hidden");
|
||||
$(".container.vote").removeClass("hidden");
|
||||
$(".loading-container").hide();
|
||||
|
||||
votingKey = $(".key-select").val();
|
||||
getBallots(web3,
|
||||
"getBallots()",
|
||||
votingKey,
|
||||
contractAddress,
|
||||
function(_ballotsArray) {
|
||||
ballotsArrayFiltered = _ballotsArray;
|
||||
showBallotsPage(_ballotsArray, web3, contractAddress);
|
||||
}
|
||||
);
|
||||
|
||||
// ballots list nav filters onclick events
|
||||
$(".nav-i").on("click", function() {
|
||||
$(".search-input").val('');
|
||||
$(".loading-container").show();
|
||||
if ($(this).hasClass("nav-i_actual")) {
|
||||
$(".nav-i").removeClass("nav-i_active");
|
||||
$(this).addClass("nav-i_active");
|
||||
getBallotsList(web3, contractAddress);
|
||||
} else if ($(this).hasClass("nav-i_unanswered")) {
|
||||
$(".nav-i").removeClass("nav-i_active");
|
||||
$(this).addClass("nav-i_active");
|
||||
getBallotsList(web3, contractAddress, {filter: "unanswered"});
|
||||
} else if ($(this).hasClass("nav-i_expired")) {
|
||||
$(".nav-i").removeClass("nav-i_active");
|
||||
$(this).addClass("nav-i_active");
|
||||
getBallotsList(web3, contractAddress, {filter: "expired"});
|
||||
}
|
||||
});
|
||||
|
||||
// search input onkeyup event
|
||||
$(".search-input").on("keyup", function() {
|
||||
var searchInput = $(this).val();
|
||||
var ballotsArrayFiltered = filterBallots(searchInput);
|
||||
$(".container.vote").empty();
|
||||
showBallotsPage(ballotsArrayFiltered, web3, contractAddress);
|
||||
});
|
||||
});
|
||||
|
||||
if (readCookie('votingKey')) {
|
||||
votingKey = readCookie('votingKey');
|
||||
$(".choose-key-button").trigger("click");
|
||||
}
|
||||
for (var i = 0; i < accounts.length; i++) {
|
||||
if (i == 0) votingKey = accounts[i];
|
||||
if (readCookie('votingKey') == accounts[i] || (!readCookie('votingKey') && i == 0)) {
|
||||
$option = "<option name='key' value=" + accounts[i] + " selected>" + accounts[i] + "</option>";
|
||||
votingKey = accounts[i];
|
||||
} else
|
||||
$option = "<option name='key' value=" + accounts[i] + ">" + accounts[i] + "</option>";
|
||||
$(".key-select").append($option);
|
||||
}
|
||||
|
||||
//back button onclick event
|
||||
$(".back").on("click", function() {
|
||||
if ($(".new-ballot-add").attr("step") == 2) {
|
||||
$(".new-ballot-inputs").removeClass("hidden");
|
||||
$(".personal-data-inputs").addClass("hidden");
|
||||
$(".new-ballot-add").attr("step", 1);
|
||||
$(".new-ballot-add").html("Continue");
|
||||
} else {
|
||||
ballotsNavPan();
|
||||
getBallotsList(web3, contractAddress);
|
||||
}
|
||||
});
|
||||
|
||||
//key select onchange event
|
||||
$(".key-select").change(function() {
|
||||
createCookie('votingKey', $(this).val(), 365);
|
||||
votingKey = $(this).val();
|
||||
});
|
||||
|
||||
//settings button onclick event
|
||||
$(".header-settings").on("click", function() {
|
||||
$(".key-content").removeClass("hidden");
|
||||
$(".content").addClass("hidden");
|
||||
});
|
||||
|
||||
//new ballot button onclick event
|
||||
$(".header-new-ballot").on("click", function() {
|
||||
$(".key-content").addClass("hidden");
|
||||
$(".content").removeClass("hidden");
|
||||
$(".container.new-ballot").removeClass("hidden");
|
||||
$(".container.vote").addClass("hidden");
|
||||
$(".container.new-ballot").empty();
|
||||
$(".container.new-ballot").load("./newBallot.html", function() {
|
||||
newBallotNavPan();
|
||||
|
||||
$("#type_add").click(function() {
|
||||
$(".new-ballot-add").attr("step", 1);
|
||||
$(".new-ballot-add").html("Continue");
|
||||
});
|
||||
|
||||
$("#type_remove").click(function() {
|
||||
$(".new-ballot-add").attr("step", 2);
|
||||
$(".new-ballot-add").html("Add Ballot");
|
||||
});
|
||||
|
||||
$(".new-ballot-add").on("click", function() {
|
||||
if ($(this).attr("step") == 1) {
|
||||
$(".new-ballot-inputs").addClass("hidden");
|
||||
$(".personal-data-inputs").removeClass("hidden");
|
||||
$(".new-ballot-add").attr("step", 2);
|
||||
$(".new-ballot-add").html("Add Ballot");
|
||||
return;
|
||||
}
|
||||
|
||||
$(".loading-container").show();
|
||||
var addAction = $("input[name=type]:checked").val();
|
||||
var ballotViewObj = {
|
||||
ballotID: generateBallotID(),
|
||||
memo: $("#memo").val(),
|
||||
miningKey: $("#mining-key").val(),
|
||||
affectedKey: $("#affected-key").val(),
|
||||
affectedKeyType: parseInt($("#affected-key-type").val()),
|
||||
owner: votingKey,
|
||||
addAction: addAction
|
||||
};
|
||||
|
||||
var isAddress1 = web3.utils.isAddress($("#mining-key").val());
|
||||
var isAddress2 = web3.utils.isAddress($("#affected-key").val());
|
||||
if (!isAddress1 || !isAddress2) {
|
||||
$(".loading-container").hide();
|
||||
showAlert(null, "One or both keys are incorrect");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!addAction) {
|
||||
addBallotClick(web3, ballotViewObj, null, contractAddress);
|
||||
} else {
|
||||
var validatorViewObj = {
|
||||
miningKey: $("#mining-key").val(),
|
||||
fullName: $("#full-name").val(),
|
||||
streetName: $("#address").val(),
|
||||
state: $("#state").val(),
|
||||
zip: $("#zip").val(),
|
||||
licenseID: $("#license-id").val(),
|
||||
licenseExpiredAt: new Date($("#license-expiration").val()).getTime() / 1000,
|
||||
};
|
||||
addBallotClick(web3, ballotViewObj, validatorViewObj, contractAddress);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//triggers after clicking "Add Ballot" button
|
||||
function addBallotClick(web3, ballotViewObj, validatorViewObj, contractAddress) {
|
||||
addBallot(web3,
|
||||
"addBallot(uint256,address,address,address,uint256,bool,string)",
|
||||
ballotViewObj,
|
||||
votingKey,
|
||||
contractAddress,
|
||||
function(txHash, err) {
|
||||
addBallotCallBack(err, web3, txHash, ballotViewObj.addAction, validatorViewObj, contractAddress);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
//Adding of ballot to contract callback
|
||||
function addBallotCallBack(err, web3, txHash, addAction, validatorViewObj, contractAddress) {
|
||||
if (err) {
|
||||
$(".loading-container").hide();
|
||||
showAlert(err, err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!addAction) {
|
||||
getTxCallBack(txHash, function() {
|
||||
$(".loading-container").hide();
|
||||
$(".back").trigger("click");
|
||||
});
|
||||
} else {
|
||||
addValidator(web3,
|
||||
"addValidator(address,uint256,uint256,uint256,string,string,string)",
|
||||
validatorViewObj,
|
||||
votingKey,
|
||||
contractAddress,
|
||||
function(txHash, err) {
|
||||
addValidatorCallBack(err, txHash, web3, contractAddress);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//Adding of validator to contract callback
|
||||
function addValidatorCallBack(err, txHash, web3, contractAddress) {
|
||||
if (err) {
|
||||
$(".loading-container").hide();
|
||||
showAlert(err, err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
getTxCallBack(txHash, function() {
|
||||
$(".loading-container").hide();
|
||||
//$(".back").trigger("click");
|
||||
ballotsNavPan();
|
||||
getBallotsList(web3, contractAddress);
|
||||
});
|
||||
}
|
||||
|
||||
//shows page with list of ballots
|
||||
function showBallotsPage(_ballotsArray, web3, contractAddress) {
|
||||
for(var i = 0; i < _ballotsArray.length; i++) {
|
||||
var ballot = _ballotsArray[i];
|
||||
if (ballot) {
|
||||
var ballotID = Object.keys(ballot)[0];
|
||||
var ballotPropsObj = ballot[ballotID];
|
||||
var ballotView = getBallotView(votingKey, ballotID, ballotPropsObj, false);
|
||||
$(".container.vote").append(ballotView);
|
||||
}
|
||||
}
|
||||
|
||||
$(".loading-container").hide();
|
||||
|
||||
//vote now button onclick event
|
||||
$(".vote-now").on("click", function() {
|
||||
getBallotView(votingKey, $(this).attr("ballot-id"), null, true, web3, contractAddress, function(ballotView) {
|
||||
showSingleBallotPage(ballotView, web3, contractAddress);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//shows page with single ballot
|
||||
function showSingleBallotPage(ballotView, web3, contractAddress) {
|
||||
$(".container.vote").empty();
|
||||
$(".container.vote").append(ballotView);
|
||||
newBallotNavPan();
|
||||
|
||||
//vote button onclick event
|
||||
$(".vote-button").on("click", function(e) {
|
||||
voteButtonClick(web3, contractAddress, e, $(this));
|
||||
});
|
||||
}
|
||||
|
||||
//triggers after .vote-button clicked
|
||||
function voteButtonClick(web3, contractAddress, e, $this) {
|
||||
$(".loading-container").show();
|
||||
var voteFor = $this.hasClass("vote-rating-yes")?true:false;
|
||||
|
||||
var ballotID = $this.closest(".vote-i").attr("ballot-id");
|
||||
|
||||
vote(web3,
|
||||
"vote(uint256,bool)",
|
||||
ballotID,
|
||||
voteFor,
|
||||
votingKey,
|
||||
contractAddress,
|
||||
function(txHash, err) {
|
||||
if (err) {
|
||||
$(".loading-container").hide();
|
||||
showAlert(err, "You are already voted or have no rights to vote");
|
||||
return;
|
||||
}
|
||||
|
||||
getTxCallBack(txHash, function() {
|
||||
$(".loading-container").hide();
|
||||
$(".back").trigger("click");
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
//change to new ballot navigation pan
|
||||
function newBallotNavPan() {
|
||||
$(".nav").addClass("hidden");
|
||||
$(".search-form").addClass("hidden");
|
||||
$(".back").removeClass("hidden");
|
||||
}
|
||||
|
||||
//change to ballots navigation pan
|
||||
function ballotsNavPan() {
|
||||
$(".nav").removeClass("hidden");
|
||||
$(".search-form").removeClass("hidden");
|
||||
$(".back").addClass("hidden");
|
||||
}
|
||||
|
||||
function getBallotsList(web3, contractAddress, filterObj) {
|
||||
$(".container.new-ballot").addClass("hidden");
|
||||
$(".container.vote").removeClass("hidden");
|
||||
$(".container.vote").empty();
|
||||
getBallots(web3,
|
||||
"getBallots()",
|
||||
votingKey,
|
||||
contractAddress,
|
||||
function(_ballotsArray) {
|
||||
$(".loading-container").hide();
|
||||
if (!filterObj) {
|
||||
ballotsArrayFiltered = _ballotsArray;
|
||||
showBallotsPage(ballotsArrayFiltered, web3, contractAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
if (filterObj.filter == "expired") {
|
||||
var _ballotsArrayFiltered = [];
|
||||
for (var i = 0; i < _ballotsArray.length; i++) {
|
||||
var ballot = _ballotsArray[i];
|
||||
var ballotID = Object.keys(ballot)[0];
|
||||
if (new Date(ballot[ballotID].votingEnd*1000) < new (Date)) { //expired
|
||||
_ballotsArrayFiltered.push(ballot);
|
||||
}
|
||||
}
|
||||
ballotsArrayFiltered = _ballotsArrayFiltered;
|
||||
showBallotsPage(ballotsArrayFiltered, web3, contractAddress);
|
||||
} else if (filterObj.filter == "unanswered") {
|
||||
var _ballotsArrayFiltered = [];
|
||||
for (var i = 0; i < _ballotsArray.length; i++) {
|
||||
var ballot = _ballotsArray[i];
|
||||
var ballotID = Object.keys(ballot)[0];
|
||||
if (!ballot[ballotID].voted && (new Date(ballot[ballotID].votingEnd*1000) >= new (Date))) {
|
||||
_ballotsArrayFiltered.push(ballot);
|
||||
}
|
||||
}
|
||||
ballotsArrayFiltered = _ballotsArrayFiltered;
|
||||
showBallotsPage(ballotsArrayFiltered, web3, contractAddress);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
getWeb3(startDapp);
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
function vote(web3, func, ballotID, action, address, contractAddr, cb) {
|
||||
var funcParamsNumber = 2;
|
||||
var standardLength = 32;
|
||||
|
||||
SHA3Encrypt(web3, func, function(funcEncode) {
|
||||
var funcEncodePart = funcEncode.substring(0,10);
|
||||
|
||||
var data = funcEncodePart
|
||||
+ toUnifiedLengthLeft(ballotID.toString(16))
|
||||
+ toUnifiedLengthLeft((+action).toString());
|
||||
|
||||
estimateGas(web3, address, contractAddr, data, function(estimatedGas, err) {
|
||||
if (err) return cb(null, err);
|
||||
estimatedGas += 100000;
|
||||
|
||||
sendTx(web3, address, contractAddr, data, estimatedGas, function(txHash, err) {
|
||||
if (err) return cb(txHash, err);
|
||||
cb(txHash);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"environment": "live",
|
||||
"networkID": 12648430,
|
||||
"Ethereum": {
|
||||
"live": {
|
||||
"contractAddress": "0xf472e0e43570b9afaab67089615080cf7c20018d"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
@import './index/*';
|
|
@ -1,43 +0,0 @@
|
|||
%stretch-width {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
%btn {
|
||||
cursor: pointer;
|
||||
transition: 0.3s background-color;
|
||||
border-radius: 3px;
|
||||
border: 0;
|
||||
padding: 0 15px 0 32px;
|
||||
background-color: #08b3f2;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left 15px center;
|
||||
color: #fff;
|
||||
line-height: 36px;
|
||||
font-size: 13px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
|
||||
&:hover {
|
||||
background-color: #20bdf7;
|
||||
}
|
||||
|
||||
&-new {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAFVBMVEX///////////////////////////9nSIHRAAAABnRSTlMASUrk5udXTd49AAAAOUlEQVR42tXQsQEAIAgDQcAn+4+snRZxAK79KokrIcNBwgYdc0Migwxk8Qsd1TJWDf/KQWobqt+9G4coA99W7as5AAAAAElFTkSuQmCC);
|
||||
background-size: 12px 12px;
|
||||
}
|
||||
|
||||
&-vote {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAASCAMAAAB2Mu6sAAAAgVBMVEX///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+xZKUEAAAAKnRSTlMAAQIDPVBWYmNkZmpxeHp7fIOEhYaHiZeZmp6foKGio6TV2dze5unx9f63Mu9fAAAAgElEQVR4AXXO1w7CMBSD4VM2lFE2lD0I1O//gIBrCUUn/Hf5rCixRI3V695KeLMEcEn4Dp+C9z2+DZ0f6IXz4x8/0Sf63c/P9DEP7fDM5VkpZw8Ao9rX9JnVBWjJNvSlqW7F67GzAZdiS1+QooXNLaqvRe/6ZWquXuVcdW7XPJY3R9MVHwqyyb8AAAAASUVORK5CYII=);
|
||||
background-size: 12px 9px;
|
||||
}
|
||||
}
|
||||
|
||||
%item {
|
||||
margin-bottom: 30px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #eee;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
display: table;
|
||||
width: 100%;
|
||||
min-width: 960px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 80px 0 60px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
background-color: #fbfbfb;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.key-content {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
background-color: #fbfbfb;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
.hidden {
|
||||
display:none;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
%title {
|
||||
color: #333;
|
||||
text-transform: uppercase;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
%description {
|
||||
color: #8197a2;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.choose-key {
|
||||
@extend %item;
|
||||
padding: 30px;
|
||||
display: inline-block;
|
||||
|
||||
h1 {
|
||||
@extend %title;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@extend %description;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&-button {
|
||||
@extend %btn;
|
||||
display: inline-block;
|
||||
background-color: #08b3f2;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAYAAACALL/6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAMLWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjarVdnVBOJFv6mJIEQakIEpATpIIooTaR3QUA6rC0kAQIhxBRU7K7LKrh2sWBFV0VcdS2grAVR17qIvftQF5WVdbF3eT8ScNf3/rxz3j1nZr5z57v3fvfOnDlzAeMhQoVCRpoAxXK1MiUmXJCVnSNgtYEACQ64sBCKVIqw5OQEAOi5/tNeXwUBAJc8hQqFDP+bmYolKhFAJAPIFatExQCxF6CtRAqlGmC0AnCYoFaoAcZLADxlVnYOwNQDwMvXYmsAvFwt9gLAU6alRADMSECPIxQq8wGjZACCUlG+GjBSAPCSi6VywGg9gGBRgVAMGLUB6F9cXCIGjDkAXHP/lif/Hzlze3MKhfm9WNsLAEAvUqpSyIST8P+2Ypmmp0Y/AJwCZWwKAB5AbC0qiU8BwAGIg/LcxCQAZgBxSioGdPhmgSY2XcfvFKkicgDwARJiYWQ8AGuA5GuK0sN02FuoBLR8MlGqjkvT4VxlSYouP1kqlyUm6PLMKZDE9eC1ElVUag8nTxodB8AEIPeWFaRlanWSx0ulGYkAjACyVVWUGq+LvVtWEJHYw1FqUtIBOALkyzxldIqWQ1kUq3r6ogaIhFGpACwAKlRdkBarjaWyJKqshB4NYklklFYDJZbI03XaKLVCHZ6iiy1XyJJ1fGqtRBaTop0ztUtVmtoTe1GtTNPNnLpfKByerNVPvVaok9O02mgaCYhAJATQQIBclKAQ0pbOhk4IdHeiIYQS+ZDAU+fpiciEEErIIUQqyvAn5JBA1RsXDiGUkKAUcnzq9WrPnsiDEEqUQgIVivAQShTTVnQwHUgn0MF0KB1Me9P+dEBPnMC4pyozihnJjGVGM916dYhQAhlKoIT0v/jiIYMEGighgbynhy/5GA8ZFxj3GVcYbYwbyMDvUELawxornaX8SrkAI9AGjW4qEuRCjo4eDu1Me9M+dDgdRAfTARDQfNoKnvQQ2p8Oo0PoQNqHDviHQk2vti+z/LqeBPJ/9KPzG7kb+ehU5PY+mYhe1tdZIv42IzFKEP81k5pD7aFOUkep09RBqgEC6gi1nzpHHaIa/vYm/A4l8nurpUACOYogg7SH41Xn1eH18T+qC3UKlJBABaglE9UAEFGimKSU5heoBWEKhUwiiJOLBvQXeHsN8gWysnME2s/HCz4IAAT/zBff+CYgoAIg8r/4hA7AgYcA9/UXn8NzgLMQONQq0ihLtT4aABhgwxg8WMIWDnCFJ7zhi0CEIgrDkYQ0ZGMMRChAMZSYgCmYiXJUYiGWYRXWYSO24ifsRgMO4ih+xVm04gpuoQ3teIIuvMYHgiBYhCHBJSwJO8KJ8CC8CX8imIgiEogUIpsYR+QTckJDTCG+JSqJxcQqYgNRS/xMHCCOEqeJC8QN4h7RQTwn3pMUySF5pA3pTA4k/ckwMp5MI0eT+eR4soycTc4nV5A15HaynjxKniWvkG3kE/IVBcqA4lP2lCflT0VQSVQOlUcpqWlUBVVF1VA7qEbqJHWJaqM6qXc0k+bSAtqTDqRj6XRaRI+np9Hz6FX0VrqePk5fou/RXfRnhiHDmuHBGMqIY2Qx8hkTGOWMKsZmxj7GCcYVRjvjNZPJ5DNdmH7MWGY2s5A5mTmPuYa5k9nEvMB8wHzFYrEsWR6sIFYSS8hSs8pZK1nbWUdYF1ntrLd6Bnp2et560Xo5enK9WXpVetv0Dutd1Huk90HfRN9Jf6h+kr5Yf5L+Av1N+o365/Xb9T+wTdku7CB2GruQPZO9gr2DfYJ9m/3CwMCgn0GAwUgDqcEMgxUGuwxOGdwzeMcx47hzIjijOBrOfM4WThPnBueFoaGhs2GoYY6h2nC+Ya3hMcO7hm+NuEYDjOKMxEbTjaqN6o0uGj011jd2Mg4zHmNcZlxlvMf4vHGnib6Js0mEidBkmkm1yQGTayavTLmmg0yTTItN55luMz1t+tiMZeZsFmUmNpttttHsmNkDLsV14EZwRdxvuZu4J7jtPCbPhRfHK+RV8n7itfC6zM3Mh5hnmE80rzY/ZN7Gp/jO/Di+jL+Av5t/lf++j02fsD6SPnP77Ohzsc8bi74WoRYSiwqLnRZXLN5bCiyjLIssF1k2WN6xoq3crUZaTbBaa3XCqrMvr29gX1Hfir67+960Jq3drVOsJ1tvtD5n/crG1ibGRmGz0uaYTact3zbUttB2qe1h2w47rl2wndRuqd0Ruz8E5oIwgUywQnBc0GVvbR9rr7HfYN9i/6GfS7/0frP67ex3x4Ht4O+Q57DUodmhy9HOcYTjFMc6x5tO+k7+TgVOy51OOr1xdnHOdP7eucH5sYuFS5xLmUudy21XQ9cQ1/GuNa6X3Zhu/m5FbmvcWt1Jdx/3Avdq9/MepIevh9RjjceF/oz+Af3l/Wv6X/PkeIZ5lnrWed4bwB+QMGDWgIYBTwc6DswZuGjgyYGfvXy8ZF6bvG4NMhs0fNCsQY2Dnnu7e4u8q70vDzYcHD14+uD9g58N8RgiGbJ2yHUfrs8In+99mn0++fr5Kn13+Hb4OfqN81vtd82f55/sP8//VAAjIDxgesDBgHdDfYeqh+4e+legZ2BR4LbAx8NchkmGbRr2IKhfkDBoQ1BbsCB4XPD64LYQ+xBhSE3I/VCHUHHo5tBHYW5hhWHbw56Ge4Urw/eFv4kYGjE1oimSioyJrIhsiTKLSo9aFXU3ul90fnRddFeMT8zkmKZYRmx87KLYa3E2caK42riu4X7Dpw4/Hs+JT41fFX8/wT1BmdA4ghwxfMSSEbcTnRLliQ1JSIpLWpJ0J9kleXzyLyOZI5NHVo98mDIoZUrKyVRu6tjUbamv08LTFqTdSndN16Q3ZxhnjMqozXiTGZm5OLMta2DW1Kyz2VbZ0uz9OaycjJzNOa++ifpm2Tfto3xGlY+6Otpl9MTRp8dYjZGNOTTWeKxw7J5xjHGZ47aN+yhMEtYIX+XG5a7O7RJFiJaLnohDxUvFHZIgyWLJo7ygvMV5j/OD8pfkdxSEFFQVdEojpKukzwpjC9cVvilKKtpS1C3LlO0s1iseV3xAbiYvkh8vsS2ZWHJB4aEoV7SNHzp+2fguZbxys4pQjVbtV/PUCvU5javmO8290uDS6tK3EzIm7JloOlE+8dwk90lzJz0qiy77cTI9WTS5eYr9lJlT7k0Nm7phGjEtd1rzdIfps6e3z4iZsXUme2bRzN9mec1aPOvlt5nfNs62mT1j9oPvYr6rKzcqV5Zf+z7w+3Vz6DnSOS1zB89dOfdzhbjiTKVXZVXlx3mieWd+GPTDih+65+fNb1ngu2DtQuZC+cKri0IWbV1surhs8YMlI5bULxUsrVj6ctnYZaerhlStW85erlnetiJhxf6VjisXrvy4qmDVlerw6p2rrVfPXf1mjXjNxbWha3ess1lXue79eun66xtiNtTXONdUbWRuLN34cFPGppM/+v9Yu9lqc+XmT1vkW9q2pmw9XutXW7vNetuCOrJOU9exfdT21p8if9q/w3PHhp38nZW7sEuz64+fx/18dXf87uY9/nt27HXau3ofd19FPVE/qb6roaChbX/2/gsHhh9obgxs3PfLgF+2HLQ/WH3I/NCCw+zDsw93Hyk78qpJ0dR5NP/og+axzbeOZR27fHzk8ZYT8SdO/Rr967GTYSePnAo6dfD00NMHzvifaTjre7b+nM+5fb/5/Lavxbel/rzf+f2tAa2NF4ZdOHwx5OLRS5GXfr0cd/nslcQrF66mX71+bdS1tuvi649vyG48u1l688OtGbcZtyvumNypumt9t+Zfbv/a2ebbduhe5L1z91Pv33ogevDkd9XvH9tnPzR8WPXI7lHtY+/HBzuiO1r/+OaP9ieKJx86y/80/XP1U9ene/8K/etcV1ZX+zPls+7n815YvtjycsjL5lfJr+6+Ln794U3FW8u3W9/5vzv5PvP9ow8TPrI+rvjk9qnxc/zn293F3d0KoVIIAKAAkHl5wPMtgGE2wG0F2Eba/QsAQGh3RkD7D/LfsXZHAwD4AltCgfQZQEITsLYJcJoBcJqAZABpoSAHD+49dKbKG+ytzcVRAoy33d0vbABWI/BJ2d39YU1396dNAHUDaBqv3fsAgGkCrHcDgJZz7Clf71//BlLWbMT30dyZAAAAIGNIUk0AAG11AABzoAAA/N0AAINkAABw6AAA7GgAADA+AAAQkOTsmeoAAABmSURBVHjalJE5CoBAEARLMV8fZGjok40Uo32NH7AMPBBxVy2YZJiG7ulCJcEELEAHzMeyJM0CNEAPhHOrpqZWoxtRDWpW8ChCHf1OzGV45sVSuFmqfx2/hR7uH1KzPVR7ee21uHUAspX6H0FQzIIAAAAASUVORK5CYII=);
|
||||
|
||||
&:hover {
|
||||
background-color: #079dd4;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
outline: none;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
|
||||
select {
|
||||
transition: 0.3s border-color;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #eee;
|
||||
|
||||
&:focus {
|
||||
border-color: #08b3f2;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 0 15px;
|
||||
height: 36px;
|
||||
font-size: 11px;
|
||||
background: #fff;
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
.footer {
|
||||
@extend %stretch-width;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 0;
|
||||
padding: 15px 10px;
|
||||
color: #fff;
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
background-image: url(../images/bg_footer.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-logo {
|
||||
@include image-2x('../images/GOVERNANCE_footer@2x.png', 100px, 24px);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 100px;
|
||||
height: 24px;
|
||||
background-image: url(../images/GOVERNANCE_footer.png);
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
&-rights {
|
||||
@extend %stretch-width;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
.header {
|
||||
@extend %stretch-width;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
padding: 18px 10px;
|
||||
background-image: url(../images/bg_header.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
|
||||
&-settings {
|
||||
float: right;
|
||||
text-transform: uppercase;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
line-height: 39px;
|
||||
margin-right: 40px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&-logo {
|
||||
@include image-2x('../images/GOVERNANCE@2x.png', 149px, 35px);
|
||||
float: left;
|
||||
width: 149px;
|
||||
height: 35px;
|
||||
background-image: url(../images/GOVERNANCE.png);
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
&-new-ballot {
|
||||
@extend %btn;
|
||||
@extend %btn-new;
|
||||
float: right;
|
||||
margin-top: 3px;
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
.new-ballot {
|
||||
@extend %item;
|
||||
padding: 10px 20px;
|
||||
margin-bottom: 30px;
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
|
||||
&-inputs {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.left,
|
||||
.right {
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
label {
|
||||
&:not(.radio) {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
margin-top: 20px;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
textarea {
|
||||
outline: none;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
transition: 0.3s border-color;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #eee;
|
||||
|
||||
&:focus {
|
||||
border-color: #08b3f2;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
padding: 15px;
|
||||
height: 110px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 0 15px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
display: none;
|
||||
|
||||
&:checked + .radio:after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.radio {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
padding-left: 30px;
|
||||
margin-top: 20px;
|
||||
margin-right: 40px;
|
||||
font-size: 12px;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&:before {
|
||||
left: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #d8d9db;
|
||||
}
|
||||
|
||||
&:after {
|
||||
transition: 0.3s opacity;
|
||||
opacity: 0;
|
||||
left: 5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-top: -5px;
|
||||
background-color: #08b3f2;
|
||||
}
|
||||
}
|
||||
|
||||
&-description {
|
||||
margin-top: 10px;
|
||||
color: #8197a2;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
&-add {
|
||||
@extend %btn;
|
||||
@extend %btn-vote;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
.socials {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
float: right;
|
||||
font-size: 0;
|
||||
|
||||
&-i {
|
||||
transition: 0.3s background-color;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-left: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: fade-out(#fff, 0.8);
|
||||
|
||||
&:hover {
|
||||
@media screen and (min-width: 768px) {
|
||||
background-color: fade-out(#fff, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
&:before {
|
||||
@include image-2x('../images/socials@2x.png', 15px, 40px);
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
background-image: url(../images/socials.png);
|
||||
}
|
||||
|
||||
&_reddit {
|
||||
&:before {
|
||||
width: 15px;
|
||||
height: 13px;
|
||||
margin: -6.5px 0 0 -7.5px;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
}
|
||||
|
||||
&_twitter {
|
||||
&:before {
|
||||
width: 15px;
|
||||
height: 12px;
|
||||
margin: -6px 0 0 -7.5px;
|
||||
background-position: 0 -28px;
|
||||
}
|
||||
}
|
||||
|
||||
&_bitcoin {
|
||||
&:before {
|
||||
width: 11px;
|
||||
height: 15px;
|
||||
margin: -7.5px 0 0 -5.5px;
|
||||
background-position: 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,285 +0,0 @@
|
|||
.vote {
|
||||
text-align: left;
|
||||
|
||||
&-i {
|
||||
@extend %item;
|
||||
}
|
||||
|
||||
&-body {
|
||||
display: table;
|
||||
width: 100%;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 0;
|
||||
|
||||
&-i {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
width: 33.333%;
|
||||
box-sizing: border-box;
|
||||
padding: 30px 20px;
|
||||
|
||||
&:not(:first-child) {
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
|
||||
&:nth-child(2n) {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
%title {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
color: #8197a2;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&-title {
|
||||
@extend %title;
|
||||
|
||||
&-secondary {
|
||||
@extend %title;
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&-description {
|
||||
color: #444;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
overflow: hidden;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
&-now {
|
||||
@extend %btn;
|
||||
@extend %btn-vote;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
&-person {
|
||||
position: relative;
|
||||
padding-left: 50px;
|
||||
|
||||
&-img {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
&-name {
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&-choose {
|
||||
padding-left: 20px;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAMAAABF0y+mAAAAw1BMVEWAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6OAl6M9jT1MAAAAQHRSTlMAAQIDBQYHDA0bHB8gISQmJyg+P0BGR0lKU1RZYmNrbHV2iYqLo6Spqqusra7Cw8TFysvM29zk5ufo6/Lz+Pn8DzgVHgAAATNJREFUeNplk+1agkAQhQfRykQTrJTEUjSCvtTQECPk/q+qPQ7LsnR+7DOzL898LiRlOP46zfN0vXQM0tXydkWlndeqs/620LTtK3Z7LBo6OpLd5fDzl6nVubSnIXv3zIY/8N4HVGrwBj8bwjaR77SgmhYn5DWF5eE7ZoribkZkoIeI78ZJMmYrQkcGjZD9mq+SokjY6qKqEfniDIiFYMR6FqZPn+J8+A9dYX5QKs5eBaFz3p4wUkLwizrkvB2UcoZXJUwYHiqIsDclnBzAvicyrFYQpBW0lK0oqVbkEHR1fzEEHl/YhCHG1yKaoYi5zubl4Mn8wsqe6uwRK4tNmHaG717VsiP4mV1uih9G4FrttuUG7GGGTLOioUwyRI51Ftv6o94rtNcfNbCz2uB32KycCv0BfU5XCrQEyKwAAAAASUVORK5CYII=);
|
||||
background-size: 14px 14px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
color: #8197a2;
|
||||
line-height: 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-time {
|
||||
color: #8197a2;
|
||||
|
||||
&-timer {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&-to {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-rating {
|
||||
overflow: hidden;
|
||||
padding: 30px 20px;
|
||||
|
||||
&-i {
|
||||
width: 48%;
|
||||
color: #8197a2;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
|
||||
strong {
|
||||
margin-right: 5px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
&-value,
|
||||
&-got {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&-scale {
|
||||
clear: left;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: #efefef;
|
||||
|
||||
$this: &;
|
||||
|
||||
&-active {
|
||||
height: 100%;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
&_yes {
|
||||
#{$this}-active {
|
||||
background-color: #08b3f2;
|
||||
}
|
||||
}
|
||||
|
||||
&_no {
|
||||
#{$this}-active {
|
||||
background-color: #6d2eae;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-tooltip {
|
||||
transform: translate3d(0,-10px,0);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
transition: opacity 0.3s, transform 0.3s;
|
||||
left: -275px;
|
||||
top: 0;
|
||||
width: 578px;
|
||||
padding-top: 30px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:before {
|
||||
transform: rotate(-45deg);
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
left: 50%;
|
||||
top: 22px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: -12px;
|
||||
border-radius: 2px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 0 20px 0 fade-out(#000, 0.9);
|
||||
}
|
||||
|
||||
$this: &;
|
||||
|
||||
&-text {
|
||||
display: block;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
padding: 25px;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
&-shadow {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 30px;
|
||||
bottom: 0;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 20px 40px 0 fade-out(#000, 0.7);
|
||||
}
|
||||
|
||||
&-title {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&-description {
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
text-transform: none;
|
||||
|
||||
a {
|
||||
color: #08b3f2;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-top: -6px;
|
||||
border-radius: 50%;
|
||||
background-color: #08b3f2;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAYCAMAAADqO6ysAAAAFVBMVEX///////////////////////////9nSIHRAAAABnRSTlMASUrk5udXTd49AAAAJUlEQVQI12NgZGFhZGBhY2MBEawMTKzMTAzYAEIdGxCQS8DtAABR5AGfy9XNLgAAAABJRU5ErkJggg==);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 2px 12px;
|
||||
}
|
||||
|
||||
&-container {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
float: right;
|
||||
|
||||
&:hover {
|
||||
#{$this} {
|
||||
pointer-events: auto;
|
||||
opacity: 1;
|
||||
transform: translate3d(0,0,0) rotate3d(0,0,0,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.vote-rating-yes,
|
||||
.vote-rating-no {
|
||||
@extend %btn;
|
||||
@extend %btn-vote;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.vote-rating-no {
|
||||
background-color: #6d2eae;
|
||||
|
||||
&:hover {
|
||||
background-color: #5d2795;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Make sure that including paths.js after env.js will read .env variables.
|
||||
delete require.cache[require.resolve('./paths')];
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
if (!NODE_ENV) {
|
||||
throw new Error(
|
||||
'The NODE_ENV environment variable is required but was not specified.'
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||
var dotenvFiles = [
|
||||
`${paths.dotenv}.${NODE_ENV}.local`,
|
||||
`${paths.dotenv}.${NODE_ENV}`,
|
||||
// Don't include `.env.local` for `test` environment
|
||||
// since normally you expect tests to produce the same
|
||||
// results for everyone
|
||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
|
||||
paths.dotenv,
|
||||
].filter(Boolean);
|
||||
|
||||
// Load environment variables from .env* files. Suppress warnings using silent
|
||||
// if this file is missing. dotenv will never modify any environment variables
|
||||
// that have already been set.
|
||||
// https://github.com/motdotla/dotenv
|
||||
dotenvFiles.forEach(dotenvFile => {
|
||||
if (fs.existsSync(dotenvFile)) {
|
||||
require('dotenv').config({
|
||||
path: dotenvFile,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// We support resolving modules according to `NODE_PATH`.
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/253.
|
||||
// It works similar to `NODE_PATH` in Node itself:
|
||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
|
||||
// We also resolve them to make sure all tools using them work consistently.
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
process.env.NODE_PATH = (process.env.NODE_PATH || '')
|
||||
.split(path.delimiter)
|
||||
.filter(folder => folder && !path.isAbsolute(folder))
|
||||
.map(folder => path.resolve(appDirectory, folder))
|
||||
.join(path.delimiter);
|
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in Webpack configuration.
|
||||
const REACT_APP = /^REACT_APP_/i;
|
||||
|
||||
function getClientEnvironment(publicUrl) {
|
||||
const raw = Object.keys(process.env)
|
||||
.filter(key => REACT_APP.test(key))
|
||||
.reduce(
|
||||
(env, key) => {
|
||||
env[key] = process.env[key];
|
||||
return env;
|
||||
},
|
||||
{
|
||||
// Useful for determining whether we’re running in production mode.
|
||||
// Most importantly, it switches React into the correct mode.
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
// Useful for resolving the correct path to static assets in `public`.
|
||||
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: publicUrl,
|
||||
}
|
||||
);
|
||||
// Stringify all values so we can feed into Webpack DefinePlugin
|
||||
const stringified = {
|
||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
||||
env[key] = JSON.stringify(raw[key]);
|
||||
return env;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
return { raw, stringified };
|
||||
}
|
||||
|
||||
module.exports = getClientEnvironment;
|
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
// This is a custom Jest transformer turning style imports into empty objects.
|
||||
// http://facebook.github.io/jest/docs/tutorial-webpack.html
|
||||
|
||||
module.exports = {
|
||||
process() {
|
||||
return 'module.exports = {};';
|
||||
},
|
||||
getCacheKey() {
|
||||
// The output is always the same.
|
||||
return 'cssTransform';
|
||||
},
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
// This is a custom Jest transformer turning file imports into filenames.
|
||||
// http://facebook.github.io/jest/docs/tutorial-webpack.html
|
||||
|
||||
module.exports = {
|
||||
process(src, filename) {
|
||||
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
|
||||
},
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const url = require('url');
|
||||
|
||||
// Make sure any symlinks in the project folder are resolved:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/637
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
|
||||
|
||||
const envPublicUrl = process.env.PUBLIC_URL;
|
||||
|
||||
function ensureSlash(path, needsSlash) {
|
||||
const hasSlash = path.endsWith('/');
|
||||
if (hasSlash && !needsSlash) {
|
||||
return path.substr(path, path.length - 1);
|
||||
} else if (!hasSlash && needsSlash) {
|
||||
return `${path}/`;
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
const getPublicUrl = appPackageJson =>
|
||||
envPublicUrl || require(appPackageJson).homepage;
|
||||
|
||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||
// "public path" at which the app is served.
|
||||
// Webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
function getServedPath(appPackageJson) {
|
||||
const publicUrl = getPublicUrl(appPackageJson);
|
||||
const servedUrl =
|
||||
envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/');
|
||||
return ensureSlash(servedUrl, true);
|
||||
}
|
||||
|
||||
// config after eject: we're in ./config/
|
||||
module.exports = {
|
||||
dotenv: resolveApp('.env'),
|
||||
appBuild: resolveApp('build'),
|
||||
appPublic: resolveApp('public'),
|
||||
appHtml: resolveApp('public/index.html'),
|
||||
appIndexJs: resolveApp('src/index.js'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveApp('src/setupTests.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
publicUrl: getPublicUrl(resolveApp('package.json')),
|
||||
servedPath: getServedPath(resolveApp('package.json')),
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
'use strict';
|
||||
|
||||
if (typeof Promise === 'undefined') {
|
||||
// Rejection tracking prevents a common issue where React gets into an
|
||||
// inconsistent state due to an error, but it gets swallowed by a Promise,
|
||||
// and the user has no idea what causes React's erratic future behavior.
|
||||
require('promise/lib/rejection-tracking').enable();
|
||||
window.Promise = require('promise/lib/es6-extensions.js');
|
||||
}
|
||||
|
||||
// fetch() polyfill for making API calls.
|
||||
require('whatwg-fetch');
|
||||
|
||||
// Object.assign() is commonly used with React.
|
||||
// It will use the native implementation if it's present and isn't buggy.
|
||||
Object.assign = require('object-assign');
|
||||
|
||||
// In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
|
||||
// We don't polyfill it in the browser--this is user's responsibility.
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
require('raf').polyfill(global);
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
'use strict';
|
||||
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
|
||||
const eslintFormatter = require('react-dev-utils/eslintFormatter');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const getClientEnvironment = require('./env');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// In development, we always serve from the root. This makes config easier.
|
||||
const publicPath = '/';
|
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
|
||||
const publicUrl = '';
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(publicUrl);
|
||||
|
||||
// This is the development configuration.
|
||||
// It is focused on developer experience and fast rebuilds.
|
||||
// The production configuration is different and lives in a separate file.
|
||||
module.exports = {
|
||||
// You may want 'eval' instead if you prefer to see the compiled output in DevTools.
|
||||
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
|
||||
devtool: 'cheap-module-source-map',
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
// The first two entry points enable "hot" CSS and auto-refreshes for JS.
|
||||
entry: [
|
||||
// We ship a few polyfills by default:
|
||||
require.resolve('./polyfills'),
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
],
|
||||
output: {
|
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
pathinfo: true,
|
||||
// This does not produce a real file. It's just the virtual path that is
|
||||
// served by WebpackDevServer in development. This is the JS bundle
|
||||
// containing code from all our entry points, and the Webpack runtime.
|
||||
filename: 'static/js/bundle.js',
|
||||
// There are also additional JS chunk files if you use code splitting.
|
||||
chunkFilename: 'static/js/[name].chunk.js',
|
||||
// This is the URL that app is served from. We use "/" in development.
|
||||
publicPath: publicPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: info =>
|
||||
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
|
||||
alias: {
|
||||
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
},
|
||||
plugins: [
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// TODO: Disable require.ensure as it's not a standard language feature.
|
||||
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
|
||||
// { parser: { requireEnsure: false } },
|
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
enforce: 'pre',
|
||||
use: [
|
||||
{
|
||||
options: {
|
||||
formatter: eslintFormatter,
|
||||
eslintPath: require.resolve('eslint'),
|
||||
|
||||
},
|
||||
loader: require.resolve('eslint-loader'),
|
||||
},
|
||||
],
|
||||
include: paths.appSrc,
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process JS with Babel.
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
|
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
// directory for faster rebuilds.
|
||||
cacheDirectory: true,
|
||||
},
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use a plugin to extract that CSS to a file, but
|
||||
// in development "style" loader enables hot editing of CSS.
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
require.resolve('style-loader'),
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
autoprefixer({
|
||||
browsers: [
|
||||
'>1%',
|
||||
'last 4 versions',
|
||||
'Firefox ESR',
|
||||
'not ie < 9', // React doesn't support IE8 anyway
|
||||
],
|
||||
flexbox: 'no-2009',
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// it's runtime that would otherwise processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.js$/, /\.html$/, /\.json$/],
|
||||
loader: require.resolve('file-loader'),
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(env.raw),
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
}),
|
||||
// Add module names to factory functions so they appear in browser profiler.
|
||||
new webpack.NamedModulesPlugin(),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/240
|
||||
new CaseSensitivePathsPlugin(),
|
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for Webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/186
|
||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
],
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
dgram: 'empty',
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
// Turn off performance hints during development because we don't do any
|
||||
// splitting or minification in interest of speed. These warnings become
|
||||
// cumbersome.
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,342 @@
|
|||
'use strict';
|
||||
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
|
||||
const eslintFormatter = require('react-dev-utils/eslintFormatter');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const paths = require('./paths');
|
||||
const getClientEnvironment = require('./env');
|
||||
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
const publicPath = paths.servedPath;
|
||||
// Some apps do not use client-side routing with pushState.
|
||||
// For these, "homepage" can be set to "." to enable relative asset paths.
|
||||
const shouldUseRelativeAssetPaths = publicPath === './';
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
const publicUrl = publicPath.slice(0, -1);
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(publicUrl);
|
||||
|
||||
// Assert this just to be safe.
|
||||
// Development builds of React are slow and not intended for production.
|
||||
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
|
||||
throw new Error('Production builds must have NODE_ENV=production.');
|
||||
}
|
||||
|
||||
// Note: defined here because it will be used more than once.
|
||||
const cssFilename = 'static/css/[name].[contenthash:8].css';
|
||||
|
||||
// ExtractTextPlugin expects the build output to be flat.
|
||||
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
|
||||
// However, our output is structured with css, js and media folders.
|
||||
// To have this structure working with relative paths, we have to use custom options.
|
||||
const extractTextPluginOptions = shouldUseRelativeAssetPaths
|
||||
? // Making sure that the publicPath goes back to to build folder.
|
||||
{ publicPath: Array(cssFilename.split('/').length).join('../') }
|
||||
: {};
|
||||
|
||||
// This is the production configuration.
|
||||
// It compiles slowly and is focused on producing a fast and minimal bundle.
|
||||
// The development configuration is different and lives in a separate file.
|
||||
module.exports = {
|
||||
// Don't attempt to continue if there are any errors.
|
||||
bail: true,
|
||||
// We generate sourcemaps in production. This is slow but gives good results.
|
||||
// You can exclude the *.map files from the build during deployment.
|
||||
devtool: shouldUseSourceMap ? 'source-map' : false,
|
||||
// In production, we only want to load the polyfills and the app code.
|
||||
entry: [require.resolve('./polyfills'), paths.appIndexJs],
|
||||
output: {
|
||||
// The build folder.
|
||||
path: paths.appBuild,
|
||||
// Generated JS file names (with nested folders).
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
// We don't currently advertise code splitting but Webpack supports it.
|
||||
filename: 'static/js/[name].[chunkhash:8].js',
|
||||
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath: publicPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: info =>
|
||||
path
|
||||
.relative(paths.appSrc, info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/'),
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
|
||||
alias: {
|
||||
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
},
|
||||
plugins: [
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// TODO: Disable require.ensure as it's not a standard language feature.
|
||||
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
|
||||
// { parser: { requireEnsure: false } },
|
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
enforce: 'pre',
|
||||
use: [
|
||||
{
|
||||
options: {
|
||||
formatter: eslintFormatter,
|
||||
eslintPath: require.resolve('eslint'),
|
||||
|
||||
},
|
||||
loader: require.resolve('eslint-loader'),
|
||||
},
|
||||
],
|
||||
include: paths.appSrc,
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works just like "file" loader but it also embeds
|
||||
// assets smaller than specified size as data URLs to avoid requests.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process JS with Babel.
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
|
||||
compact: true,
|
||||
},
|
||||
},
|
||||
// The notation here is somewhat confusing.
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader normally turns CSS into JS modules injecting <style>,
|
||||
// but unlike in development configuration, we do something different.
|
||||
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
|
||||
// (second argument), then grabs the result CSS and puts it into a
|
||||
// separate file in our build process. This way we actually ship
|
||||
// a single CSS file in production instead of JS code injecting <style>
|
||||
// tags. If you use code splitting, however, any async bundles will still
|
||||
// use the "style" loader inside the async code so CSS from them won't be
|
||||
// in the main CSS file.
|
||||
{
|
||||
test: /\.css$/,
|
||||
loader: ExtractTextPlugin.extract(
|
||||
Object.assign(
|
||||
{
|
||||
fallback: {
|
||||
loader: require.resolve('style-loader'),
|
||||
options: {
|
||||
hmr: false,
|
||||
},
|
||||
},
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
minimize: true,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
autoprefixer({
|
||||
browsers: [
|
||||
'>1%',
|
||||
'last 4 versions',
|
||||
'Firefox ESR',
|
||||
'not ie < 9', // React doesn't support IE8 anyway
|
||||
],
|
||||
flexbox: 'no-2009',
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
extractTextPluginOptions
|
||||
)
|
||||
),
|
||||
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
|
||||
},
|
||||
// "file" loader makes sure assets end up in the `build` folder.
|
||||
// When you `import` an asset, you get its filename.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// it's runtime that would otherwise processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.js$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In production, it will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
new InterpolateHtmlPlugin(env.raw),
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV was set to production here.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// Minify the code.
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false,
|
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false,
|
||||
},
|
||||
mangle: {
|
||||
safari10: true,
|
||||
},
|
||||
output: {
|
||||
comments: false,
|
||||
// Turned on because emoji and regex is not minified properly using default
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2488
|
||||
ascii_only: true,
|
||||
},
|
||||
sourceMap: shouldUseSourceMap,
|
||||
}),
|
||||
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
|
||||
new ExtractTextPlugin({
|
||||
filename: cssFilename,
|
||||
}),
|
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
}),
|
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the Webpack build.
|
||||
new SWPrecacheWebpackPlugin({
|
||||
// By default, a cache-busting query parameter is appended to requests
|
||||
// used to populate the caches, to ensure the responses are fresh.
|
||||
// If a URL is already hashed by Webpack, then there is no concern
|
||||
// about it being stale, and the cache-busting can be skipped.
|
||||
dontCacheBustUrlsMatching: /\.\w{8}\./,
|
||||
filename: 'service-worker.js',
|
||||
logger(message) {
|
||||
if (message.indexOf('Total precache size is') === 0) {
|
||||
// This message occurs for every build and is a bit too noisy.
|
||||
return;
|
||||
}
|
||||
if (message.indexOf('Skipping static resource') === 0) {
|
||||
// This message obscures real errors so we ignore it.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2612
|
||||
return;
|
||||
}
|
||||
console.log(message);
|
||||
},
|
||||
minify: true,
|
||||
// For unknown URLs, fallback to the index page
|
||||
navigateFallback: publicUrl + '/index.html',
|
||||
// Ignores URLs starting from /__ (useful for Firebase):
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
|
||||
navigateFallbackWhitelist: [/^(?!\/__).*/],
|
||||
// Don't precache sourcemaps (they're large) and build asset manifest:
|
||||
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
|
||||
}),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
],
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
dgram: 'empty',
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,100 @@
|
|||
'use strict';
|
||||
|
||||
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
||||
const path = require('path');
|
||||
const config = require('./webpack.config.dev');
|
||||
const paths = require('./paths');
|
||||
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
|
||||
module.exports = function(proxy, allowedHost) {
|
||||
return {
|
||||
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
||||
// websites from potentially accessing local content through DNS rebinding:
|
||||
// https://github.com/webpack/webpack-dev-server/issues/887
|
||||
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
||||
// However, it made several existing use cases such as development in cloud
|
||||
// environment or subdomains in development significantly more complicated:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2271
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2233
|
||||
// While we're investigating better solutions, for now we will take a
|
||||
// compromise. Since our WDS configuration only serves files in the `public`
|
||||
// folder we won't consider accessing them a vulnerability. However, if you
|
||||
// use the `proxy` feature, it gets more dangerous because it can expose
|
||||
// remote code execution vulnerabilities in backends like Django and Rails.
|
||||
// So we will disable the host check normally, but enable it if you have
|
||||
// specified the `proxy` setting. Finally, we let you override it if you
|
||||
// really know what you're doing with a special environment variable.
|
||||
disableHostCheck:
|
||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
|
||||
// Enable gzip compression of generated files.
|
||||
compress: true,
|
||||
// Silence WebpackDevServer's own logs since they're generally not useful.
|
||||
// It will still show compile warnings and errors with this setting.
|
||||
clientLogLevel: 'none',
|
||||
// By default WebpackDevServer serves physical files from current directory
|
||||
// in addition to all the virtual build products that it serves from memory.
|
||||
// This is confusing because those files won’t automatically be available in
|
||||
// production build folder unless we copy them. However, copying the whole
|
||||
// project directory is dangerous because we may expose sensitive files.
|
||||
// Instead, we establish a convention that only files in `public` directory
|
||||
// get served. Our build script will copy `public` into the `build` folder.
|
||||
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
|
||||
// Note that we only recommend to use `public` folder as an escape hatch
|
||||
// for files like `favicon.ico`, `manifest.json`, and libraries that are
|
||||
// for some reason broken when imported through Webpack. If you just want to
|
||||
// use an image, put it in `src` and `import` it from JavaScript instead.
|
||||
contentBase: paths.appPublic,
|
||||
// By default files from `contentBase` will not trigger a page reload.
|
||||
watchContentBase: true,
|
||||
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
|
||||
// for the WebpackDevServer client so it can learn when the files were
|
||||
// updated. The WebpackDevServer client is included as an entry point
|
||||
// in the Webpack development configuration. Note that only changes
|
||||
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
||||
hot: true,
|
||||
// It is important to tell WebpackDevServer to use the same "root" path
|
||||
// as we specified in the config. In development, we always serve from /.
|
||||
publicPath: config.output.publicPath,
|
||||
// WebpackDevServer is noisy by default so we emit custom message instead
|
||||
// by listening to the compiler events with `compiler.plugin` calls above.
|
||||
quiet: true,
|
||||
// Reportedly, this avoids CPU overload on some systems.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/293
|
||||
// src/node_modules is not ignored to support absolute imports
|
||||
// https://github.com/facebookincubator/create-react-app/issues/1065
|
||||
watchOptions: {
|
||||
ignored: new RegExp(
|
||||
`^(?!${path
|
||||
.normalize(paths.appSrc + '/')
|
||||
.replace(/[\\]+/g, '\\\\')}).+[\\\\/]node_modules[\\\\/]`,
|
||||
'g'
|
||||
),
|
||||
},
|
||||
// Enable HTTPS if the HTTPS environment variable is set to 'true'
|
||||
https: protocol === 'https',
|
||||
host: host,
|
||||
overlay: false,
|
||||
historyApiFallback: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
},
|
||||
public: allowedHost,
|
||||
proxy,
|
||||
before(app) {
|
||||
// This lets us open files from the runtime error overlay.
|
||||
app.use(errorOverlayMiddleware());
|
||||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// We do this in development to avoid hitting the production cache if
|
||||
// it used the same host and port.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
|
||||
app.use(noopServiceWorkerMiddleware());
|
||||
},
|
||||
};
|
||||
};
|
BIN
docs/ballot.png
Before Width: | Height: | Size: 151 KiB |
BIN
docs/ballots.png
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 186 KiB |
Before Width: | Height: | Size: 226 KiB |
Before Width: | Height: | Size: 215 KiB |
Before Width: | Height: | Size: 215 KiB |
39
gulpfile.js
|
@ -1,39 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const sass = require('gulp-sass');
|
||||
const sassGlob = require('gulp-sass-glob');
|
||||
const autoprefixer = require('gulp-autoprefixer');
|
||||
const uglifycss = require('gulp-uglifycss');
|
||||
const include = require('gulp-include');
|
||||
const addsrc = require('gulp-add-src');
|
||||
const order = require('gulp-order');
|
||||
const concat = require('gulp-concat');
|
||||
const uglify = require('gulp-uglify');
|
||||
|
||||
gulp.task('sass', function() {
|
||||
return gulp.src(['assets/stylesheets/*.scss'])
|
||||
.pipe(sassGlob())
|
||||
.pipe(sass().on('error', sass.logError))
|
||||
.pipe(autoprefixer())
|
||||
.pipe(uglifycss())
|
||||
.pipe(gulp.dest('assets/stylesheets/'));
|
||||
});
|
||||
|
||||
gulp.task('javascript', function() {
|
||||
return gulp.src('assets/javascripts/application/*.js')
|
||||
.pipe(addsrc('assets/javascripts/vendor/index.js'))
|
||||
.pipe(order([
|
||||
"assets/javascripts/vendor/index.js",
|
||||
"assets/javascripts/application/*.js"
|
||||
], {base: '.'}))
|
||||
.pipe(include())
|
||||
.pipe(concat('application.js'))
|
||||
// .pipe(uglify())
|
||||
.pipe(gulp.dest('assets/javascripts'));
|
||||
});
|
||||
|
||||
gulp.task('watch', function() {
|
||||
gulp.watch('assets/stylesheets/**/*.scss', ['sass']);
|
||||
gulp.watch('assets/javascripts/application/*.js', ['javascript']);
|
||||
});
|
81
index.html
|
@ -1,81 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title></title>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="theme-color" content="#6151cc">
|
||||
<meta name="msapplication-config" content="./favicons/browserconfig.xml">
|
||||
<!-- <meta property="og:title" content="">
|
||||
<meta property="og:description" content=""> -->
|
||||
<!-- <meta property="og:url" content="https://oracles.org/"> -->
|
||||
<!-- <meta property="og:image" content="https://www.notarycoin.com/assets/images/share.png"> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="./assets/stylesheets/index.css">
|
||||
<link rel="apple-touch-icon" href="./favicons/favicon-192x192.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="./favicons/favicon-192x192.png">
|
||||
<link rel="mask-icon" color="#6151cc" href="./favicons/safari-pinned-tab.svg">
|
||||
<link rel="manifest" href="./favicons/manifest.webmanifest">
|
||||
<link href="./favicons/favicon.ico" rel="icon" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<a href="#" class="header-logo"></a>
|
||||
<a href="#" class="header-new-ballot">New ballot</a>
|
||||
<a href="#" class="header-settings">Settings</a>
|
||||
</div>
|
||||
</header>
|
||||
<section class="key-content">
|
||||
<div class="choose-key">
|
||||
<h1>Select voting key</h1>
|
||||
<select class="key-select">
|
||||
</select>
|
||||
<h2>
|
||||
Please select your voting key from the list. You will be able to change it later in Settings menu.
|
||||
</h2>
|
||||
<a href="#" class="choose-key-button">Continue</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="content hidden">
|
||||
<div class="search">
|
||||
<div class="container">
|
||||
<div class="nav">
|
||||
<a href="#" class="nav-i nav-i_actual nav-i_active">All</a>
|
||||
<a href="#" class="nav-i nav-i_unanswered">Unanswered</a>
|
||||
<a href="#" class="nav-i nav-i_expired">Expired</a>
|
||||
</div>
|
||||
<a href="#" class="back">Back</a>
|
||||
<form action="" class="search-form">
|
||||
<input type="text" class="search-input">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container new-ballot"></div>
|
||||
<div class="container vote"></div>
|
||||
</section>
|
||||
<div class="loading-container">
|
||||
<div class="loading">
|
||||
<div class="loading-i"></div>
|
||||
<div class="loading-i"></div>
|
||||
<div class="loading-i"></div>
|
||||
<div class="loading-i"></div>
|
||||
<div class="loading-i"></div>
|
||||
<div class="loading-i"></div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<a href="#" class="footer-logo"></a>
|
||||
<div class="socials">
|
||||
<a href="#" class="socials-i socials-i_reddit"></a>
|
||||
<a href="https://twitter.com/oraclesorg" class="socials-i socials-i_twitter"></a>
|
||||
<a href="#" class="socials-i socials-i_bitcoin"></a>
|
||||
</div>
|
||||
<p class="footer-rights">2017 Oracles Network. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="./assets/javascripts/modules.js?" type="text/javascript"></script>
|
||||
<script src="./assets/javascripts/application.js?v=1.72" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"id": "Voting",
|
||||
"name": "Voting",
|
||||
"description": "Oracles Voting",
|
||||
"version": "1.0.0",
|
||||
"author": "Oracles",
|
||||
"iconUrl": "./favicons/fav_2.png"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
let sweetalert2 = require("sweetalert2");
|
||||
let jquery = require("jquery");
|
||||
let Web3 = require("web3");
|
||||
|
||||
window.swal = sweetalert2;
|
||||
window.$ = jquery;
|
||||
window.Web3 = Web3;
|
|
@ -1,47 +0,0 @@
|
|||
<form action="">
|
||||
<div class="new-ballot-inputs">
|
||||
<div class="left">
|
||||
<label for="key">Mining key</label>
|
||||
<input type="text" id="mining-key">
|
||||
<label for="affected-key-type">Affected key type</label>
|
||||
<select type="text" id="affected-key-type" class="key-select">
|
||||
<option name="affected-key-type-select" value="0">mining key</option>
|
||||
<option name="affected-key-type-select" value="1">voting key</option>
|
||||
<option name="affected-key-type-select" value="2" selected>payout key</option>
|
||||
</select>
|
||||
<label for="affected-key">Affected key</label>
|
||||
<input type="text" id="affected-key">
|
||||
</div>
|
||||
<div class="right">
|
||||
<label for="memo">Memo</label>
|
||||
<textarea id="memo"></textarea>
|
||||
<div class="new-ballot-description">
|
||||
Please type your memo, for example, add or remove mining key, full name,
|
||||
and a reason.
|
||||
</div>
|
||||
<input type="radio" name="type" id="type_add" value=1 checked>
|
||||
<label class="radio" for="type_add">Add</label>
|
||||
<input type="radio" name="type" id="type_remove" value=0>
|
||||
<label class="radio" for="type_remove">Remove</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="personal-data-inputs hidden">
|
||||
<div class="left">
|
||||
<label for="full-name">Full name</label>
|
||||
<input type="text" id="full-name">
|
||||
<label for="address">Address</label>
|
||||
<input type="text" id="address">
|
||||
<label for="state">State</label>
|
||||
<input type="text" id="state">
|
||||
</div>
|
||||
<div class="right">
|
||||
<label for="zip">Zip code</label>
|
||||
<input type="number" id="zip">
|
||||
<label for="license-id">License id</label>
|
||||
<input type="number" id="license-id">
|
||||
<label for="license-expiration">License expiration</label>
|
||||
<input type="date" id="license-expiration">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="new-ballot-add" step=1>Continue</button>
|
||||
</form>
|
|
@ -1,40 +1,115 @@
|
|||
{
|
||||
"name": "oracles-dapps-voting",
|
||||
"version": "1.0.0",
|
||||
"description": "Oracles network Governance Dapp",
|
||||
"main": "gulpfile.js",
|
||||
"devDependencies": {
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-add-src": "^0.2.0",
|
||||
"gulp-autoprefixer": "^3.1.1",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-include": "^2.3.1",
|
||||
"gulp-order": "^1.1.1",
|
||||
"gulp-postcss": "^6.2.0",
|
||||
"gulp-sass": "^2.3.2",
|
||||
"gulp-sass-glob": "^1.0.6",
|
||||
"gulp-uglify": "^2.0.0",
|
||||
"gulp-uglifycss": "^1.0.6",
|
||||
"gulp-util": "^3.0.8",
|
||||
"http-server": "^0.9.0"
|
||||
"name": "voting",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "https://oraclesorg.github.io/poa-dapps-voting",
|
||||
"dependencies": {
|
||||
"autoprefixer": "7.1.6",
|
||||
"babel-core": "6.26.0",
|
||||
"babel-eslint": "7.2.3",
|
||||
"babel-jest": "20.0.3",
|
||||
"babel-loader": "7.1.2",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-react-app": "^3.1.0",
|
||||
"babel-preset-stage-1": "^6.24.1",
|
||||
"babel-runtime": "6.26.0",
|
||||
"case-sensitive-paths-webpack-plugin": "2.1.1",
|
||||
"chalk": "1.1.3",
|
||||
"css-loader": "0.28.7",
|
||||
"dotenv": "4.0.0",
|
||||
"eslint": "4.10.0",
|
||||
"eslint-config-react-app": "^2.0.1",
|
||||
"eslint-loader": "1.9.0",
|
||||
"eslint-plugin-flowtype": "2.39.1",
|
||||
"eslint-plugin-import": "2.8.0",
|
||||
"eslint-plugin-jsx-a11y": "5.1.1",
|
||||
"eslint-plugin-react": "7.4.0",
|
||||
"extract-text-webpack-plugin": "3.0.2",
|
||||
"file-loader": "1.1.5",
|
||||
"fs-extra": "3.0.1",
|
||||
"html-webpack-plugin": "2.29.0",
|
||||
"jest": "20.0.4",
|
||||
"mobx": "^3.4.1",
|
||||
"mobx-react": "^4.3.5",
|
||||
"mobx-react-devtools": "^4.2.15",
|
||||
"mobx-react-router": "^4.0.1",
|
||||
"moment": "^2.20.1",
|
||||
"node-sass-chokidar": "0.0.3",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"object-assign": "4.1.1",
|
||||
"postcss-flexbugs-fixes": "3.2.0",
|
||||
"postcss-loader": "2.0.8",
|
||||
"promise": "8.0.1",
|
||||
"raf": "3.4.0",
|
||||
"react": "^16.2.0",
|
||||
"react-dev-utils": "^4.2.1",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-select": "^1.1.0",
|
||||
"style-loader": "0.19.0",
|
||||
"sw-precache-webpack-plugin": "0.11.4",
|
||||
"sweetalert2": "^7.0.8",
|
||||
"url-loader": "0.6.2",
|
||||
"web3": "^1.0.0-beta.26",
|
||||
"webpack": "3.8.1",
|
||||
"webpack-dev-server": "2.9.4",
|
||||
"webpack-manifest-plugin": "1.3.2",
|
||||
"whatwg-fetch": "2.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"sass": "gulp sass",
|
||||
"coffee": "gulp javascript",
|
||||
"watch": "gulp watch",
|
||||
"start": "browserify modules.js -o assets/javascripts/modules.js && gulp javascript && gulp sass && http-server -a localhost -p 8000"
|
||||
"predeploy": "npm run build",
|
||||
"deploy": "gh-pages -d build -o origin",
|
||||
"build-css": "node-sass-chokidar src/assets/App.scss -o src/assets",
|
||||
"watch-css": "npm run build-css && node-sass-chokidar src/assets/App.scss -o src/assets/src/assets --watch",
|
||||
"start-js": "node scripts/start.js",
|
||||
"start": "npm-run-all -p watch-css start-js",
|
||||
"build-js": "node scripts/build.js",
|
||||
"build": "npm-run-all build-css build-js",
|
||||
"test": "node scripts/test.js --env=jsdom"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/oraclesorg/oracles-dapps-voting"
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{js,jsx,mjs}"
|
||||
],
|
||||
"setupFiles": [
|
||||
"<rootDir>/config/polyfills.js"
|
||||
],
|
||||
"testMatch": [
|
||||
"<rootDir>/src/**/__tests__/**/*.{js,jsx,mjs}",
|
||||
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx,mjs}"
|
||||
],
|
||||
"testEnvironment": "node",
|
||||
"testURL": "http://localhost",
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest",
|
||||
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
|
||||
"^(?!.*\\.(js|jsx|mjs|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"^react-native$": "react-native-web"
|
||||
},
|
||||
"moduleFileExtensions": [
|
||||
"web.js",
|
||||
"mjs",
|
||||
"js",
|
||||
"json",
|
||||
"web.jsx",
|
||||
"jsx",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "oraclesorg",
|
||||
"license": "MIT",
|
||||
"homepage": "https://oracles.org/",
|
||||
"dependencies": {
|
||||
"jquery": "^3.2.1",
|
||||
"sweetalert2": "^7.0.3",
|
||||
"web3": "^1.0.0-beta.26"
|
||||
"babel": {
|
||||
"presets": [
|
||||
"react-app"
|
||||
]
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
}
|
||||
}
|
||||
|
|
0
favicons/android-chrome-192x192.png → public/favicons/android-chrome-192x192.png
Normal file → Executable file
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
0
favicons/android-chrome-256x256.png → public/favicons/android-chrome-256x256.png
Normal file → Executable file
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
0
favicons/apple-touch-icon.png → public/favicons/apple-touch-icon.png
Normal file → Executable file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicons/favicon.ico">
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'production';
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const fs = require('fs-extra');
|
||||
const webpack = require('webpack');
|
||||
const config = require('../config/webpack.config.prod');
|
||||
const paths = require('../config/paths');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
|
||||
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
|
||||
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
|
||||
const printBuildError = require('react-dev-utils/printBuildError');
|
||||
|
||||
const measureFileSizesBeforeBuild =
|
||||
FileSizeReporter.measureFileSizesBeforeBuild;
|
||||
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
|
||||
// These sizes are pretty large. We'll warn for bundles exceeding them.
|
||||
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
|
||||
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// First, read the current file sizes in build directory.
|
||||
// This lets us display how much they changed later.
|
||||
measureFileSizesBeforeBuild(paths.appBuild)
|
||||
.then(previousFileSizes => {
|
||||
// Remove all content but keep the directory so that
|
||||
// if you're in it, you don't end up in Trash
|
||||
fs.emptyDirSync(paths.appBuild);
|
||||
// Merge with the public folder
|
||||
copyPublicFolder();
|
||||
// Start the webpack build
|
||||
return build(previousFileSizes);
|
||||
})
|
||||
.then(
|
||||
({ stats, previousFileSizes, warnings }) => {
|
||||
if (warnings.length) {
|
||||
console.log(chalk.yellow('Compiled with warnings.\n'));
|
||||
console.log(warnings.join('\n\n'));
|
||||
console.log(
|
||||
'\nSearch for the ' +
|
||||
chalk.underline(chalk.yellow('keywords')) +
|
||||
' to learn more about each warning.'
|
||||
);
|
||||
console.log(
|
||||
'To ignore, add ' +
|
||||
chalk.cyan('// eslint-disable-next-line') +
|
||||
' to the line before.\n'
|
||||
);
|
||||
} else {
|
||||
console.log(chalk.green('Compiled successfully.\n'));
|
||||
}
|
||||
|
||||
console.log('File sizes after gzip:\n');
|
||||
printFileSizesAfterBuild(
|
||||
stats,
|
||||
previousFileSizes,
|
||||
paths.appBuild,
|
||||
WARN_AFTER_BUNDLE_GZIP_SIZE,
|
||||
WARN_AFTER_CHUNK_GZIP_SIZE
|
||||
);
|
||||
console.log();
|
||||
|
||||
const appPackage = require(paths.appPackageJson);
|
||||
const publicUrl = paths.publicUrl;
|
||||
const publicPath = config.output.publicPath;
|
||||
const buildFolder = path.relative(process.cwd(), paths.appBuild);
|
||||
printHostingInstructions(
|
||||
appPackage,
|
||||
publicUrl,
|
||||
publicPath,
|
||||
buildFolder,
|
||||
useYarn
|
||||
);
|
||||
},
|
||||
err => {
|
||||
console.log(chalk.red('Failed to compile.\n'));
|
||||
printBuildError(err);
|
||||
process.exit(1);
|
||||
}
|
||||
);
|
||||
|
||||
// Create the production build and print the deployment instructions.
|
||||
function build(previousFileSizes) {
|
||||
console.log('Creating an optimized production build...');
|
||||
|
||||
let compiler = webpack(config);
|
||||
return new Promise((resolve, reject) => {
|
||||
compiler.run((err, stats) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
const messages = formatWebpackMessages(stats.toJson({}, true));
|
||||
if (messages.errors.length) {
|
||||
// Only keep the first error. Others are often indicative
|
||||
// of the same problem, but confuse the reader with noise.
|
||||
if (messages.errors.length > 1) {
|
||||
messages.errors.length = 1;
|
||||
}
|
||||
return reject(new Error(messages.errors.join('\n\n')));
|
||||
}
|
||||
if (
|
||||
process.env.CI &&
|
||||
(typeof process.env.CI !== 'string' ||
|
||||
process.env.CI.toLowerCase() !== 'false') &&
|
||||
messages.warnings.length
|
||||
) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'\nTreating warnings as errors because process.env.CI = true.\n' +
|
||||
'Most CI servers set it automatically.\n'
|
||||
)
|
||||
);
|
||||
return reject(new Error(messages.warnings.join('\n\n')));
|
||||
}
|
||||
return resolve({
|
||||
stats,
|
||||
previousFileSizes,
|
||||
warnings: messages.warnings,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function copyPublicFolder() {
|
||||
fs.copySync(paths.appPublic, paths.appBuild, {
|
||||
dereference: true,
|
||||
filter: file => file !== paths.appHtml,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'development';
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
const fs = require('fs');
|
||||
const chalk = require('chalk');
|
||||
const webpack = require('webpack');
|
||||
const WebpackDevServer = require('webpack-dev-server');
|
||||
const clearConsole = require('react-dev-utils/clearConsole');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const {
|
||||
choosePort,
|
||||
createCompiler,
|
||||
prepareProxy,
|
||||
prepareUrls,
|
||||
} = require('react-dev-utils/WebpackDevServerUtils');
|
||||
const openBrowser = require('react-dev-utils/openBrowser');
|
||||
const paths = require('../config/paths');
|
||||
const config = require('../config/webpack.config.dev');
|
||||
const createDevServerConfig = require('../config/webpackDevServer.config');
|
||||
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Tools like Cloud9 rely on this.
|
||||
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
|
||||
const HOST = process.env.HOST || '0.0.0.0';
|
||||
|
||||
// We attempt to use the default port but if it is busy, we offer the user to
|
||||
// run on a different port. `detect()` Promise resolves to the next free port.
|
||||
choosePort(HOST, DEFAULT_PORT)
|
||||
.then(port => {
|
||||
if (port == null) {
|
||||
// We have not found a port.
|
||||
return;
|
||||
}
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
||||
const appName = require(paths.appPackageJson).name;
|
||||
const urls = prepareUrls(protocol, HOST, port);
|
||||
// Create a webpack compiler that is configured with custom messages.
|
||||
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
|
||||
// Load proxy config
|
||||
const proxySetting = require(paths.appPackageJson).proxy;
|
||||
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
|
||||
// Serve webpack assets generated by the compiler over a web sever.
|
||||
const serverConfig = createDevServerConfig(
|
||||
proxyConfig,
|
||||
urls.lanUrlForConfig
|
||||
);
|
||||
const devServer = new WebpackDevServer(compiler, serverConfig);
|
||||
// Launch WebpackDevServer.
|
||||
devServer.listen(port, HOST, err => {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
if (isInteractive) {
|
||||
clearConsole();
|
||||
}
|
||||
console.log(chalk.cyan('Starting the development server...\n'));
|
||||
openBrowser(urls.localUrlForBrowser);
|
||||
});
|
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(function(sig) {
|
||||
process.on(sig, function() {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'test';
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.PUBLIC_URL = '';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
const jest = require('jest');
|
||||
const argv = process.argv.slice(2);
|
||||
|
||||
// Watch unless on CI or in coverage mode
|
||||
if (!process.env.CI && argv.indexOf('--coverage') < 0) {
|
||||
argv.push('--watch');
|
||||
}
|
||||
|
||||
|
||||
jest.run(argv);
|
|
@ -0,0 +1,75 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Route, NavLink } from 'react-router-dom';
|
||||
import { Header, Ballots, NewBallot, Settings, Footer } from './components';
|
||||
import './assets/App.css';
|
||||
//import DevTools from 'mobx-react-devtools'
|
||||
import Loading from './Loading';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
|
||||
@inject("commonStore")
|
||||
@observer
|
||||
class App extends Component {
|
||||
onBallotsRender = () => {
|
||||
const { commonStore } = this.props;
|
||||
commonStore.filtered = false;
|
||||
return <Ballots/>;
|
||||
}
|
||||
|
||||
onActiveBallotsRender = () => {
|
||||
const { commonStore } = this.props;
|
||||
commonStore.filtered = true;
|
||||
return <Ballots/>;
|
||||
}
|
||||
|
||||
onNewBallotRender = () => {
|
||||
return <NewBallot/>;
|
||||
}
|
||||
|
||||
onSettingsRender = () => {
|
||||
return <Settings/>;
|
||||
}
|
||||
|
||||
onSearch = (e) => {
|
||||
const { commonStore } = this.props;
|
||||
commonStore.setSearchTerm(e.target.value.toLowerCase());
|
||||
}
|
||||
|
||||
shouldShowNavPan = () => {
|
||||
const { commonStore } = this.props;
|
||||
const currentPath = this.props.location.pathname;
|
||||
let showNavPan =
|
||||
currentPath == `${commonStore.rootPath}`
|
||||
|| currentPath == `${commonStore.rootPath}/`
|
||||
|| currentPath == `${commonStore.rootPath}/active`;
|
||||
return showNavPan;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { commonStore } = this.props;
|
||||
const loading = commonStore.loading ? <Loading /> : ''
|
||||
const nav = this.shouldShowNavPan() ? <div className="search">
|
||||
<div className="container flex-container">
|
||||
<div className="nav">
|
||||
<NavLink className="nav-i" exact activeClassName="nav-i_active" to={`${commonStore.rootPath}/`}>All</NavLink>
|
||||
<NavLink className="nav-i" activeClassName="nav-i_active" to={`${commonStore.rootPath}/active`}>Active</NavLink>
|
||||
</div>
|
||||
<input type="search" className="search-input" onChange={this.onSearch}/>
|
||||
</div>
|
||||
</div> : null;
|
||||
return (
|
||||
<div>
|
||||
{loading}
|
||||
<Header />
|
||||
{nav}
|
||||
<Route exact path={`${commonStore.rootPath}/`} render={this.onBallotsRender}/>
|
||||
<Route exact path={`${commonStore.rootPath}/active`} render={this.onActiveBallotsRender}/>
|
||||
<Route path={`${commonStore.rootPath}/new`} render={this.onNewBallotRender}/>
|
||||
{/*<Route path={`${commonStore.rootPath}/settings`} render={this.onSettingsRender}/>*/}
|
||||
<Footer />
|
||||
{/*<DevTools />*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
|
@ -0,0 +1,14 @@
|
|||
import React from 'react';
|
||||
const Loading = () => (
|
||||
<div className="loading-container">
|
||||
<div className="loading">
|
||||
<div className="loading-i"></div>
|
||||
<div className="loading-i"></div>
|
||||
<div className="loading-i"></div>
|
||||
<div className="loading-i"></div>
|
||||
<div className="loading-i"></div>
|
||||
<div className="loading-i"></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
export default Loading;
|
|
@ -0,0 +1,26 @@
|
|||
@import 'stylesheets/vars';
|
||||
@import 'stylesheets/mixins';
|
||||
@import 'stylesheets/placeholders';
|
||||
@import 'stylesheets/fonts';
|
||||
@import 'stylesheets/base';
|
||||
@import 'stylesheets/controls';
|
||||
@import 'stylesheets/footer';
|
||||
@import 'stylesheets/header';
|
||||
@import 'stylesheets/info';
|
||||
@import 'stylesheets/loading';
|
||||
@import 'stylesheets/nav';
|
||||
@import 'stylesheets/new';
|
||||
@import 'stylesheets/search';
|
||||
@import 'stylesheets/settings';
|
||||
@import 'stylesheets/socials';
|
||||
@import 'stylesheets/vote-scale';
|
||||
@import 'stylesheets/ballots/placeholders';
|
||||
@import 'stylesheets/ballots/base';
|
||||
@import 'stylesheets/ballots/about';
|
||||
@import 'stylesheets/ballots/footer';
|
||||
@import 'stylesheets/select/select';
|
||||
@import 'stylesheets/select/control';
|
||||
@import 'stylesheets/select/menu';
|
||||
@import 'stylesheets/select/mixins';
|
||||
@import 'stylesheets/select/multi';
|
||||
@import 'stylesheets/select/spinner';
|
After Width: | Height: | Size: 369 B |
After Width: | Height: | Size: 669 B |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 96 B |
After Width: | Height: | Size: 120 B |
After Width: | Height: | Size: 987 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.4 KiB |