├── app ├── imgs │ └── pokemon-go.jpg ├── javascripts │ ├── index.js │ ├── npm.js │ ├── app.js │ ├── signing.js │ ├── upgrade.js │ ├── txutils.js │ ├── encryption.js │ ├── hooked-web3-provider.js │ ├── mercadopokemon.js │ └── keystore.js ├── css │ ├── jumbotron-narrow.css │ ├── bootstrap-theme.min.css │ ├── bootstrap-theme.min.css.map │ └── bootstrap-theme.css ├── player1.html └── player2.html ├── truffle.js ├── accounts-for-testing.txt ├── support ├── UTC--2015-08-09T04-06-35.465758600Z--6a5b342ec71def8aac337b82969d9ddd811023c9 └── private_genesis.json ├── contracts ├── greeter.sol ├── PokeCoin.sol ├── PokeMarket.sol └── PokeCentral.sol ├── migrations └── 1_deploy_pokemon.js ├── test └── mercado_pokemon-test.js ├── README.md └── LICENSE /app/imgs/pokemon-go.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddieoz/truffle-mercado-pokemon/HEAD/app/imgs/pokemon-go.jpg -------------------------------------------------------------------------------- /app/javascripts/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | txutils: require('./txutils.js'), 3 | encryption: require('./encryption.js'), 4 | signing: require('./signing.js'), 5 | keystore: require('./keystore.js'), 6 | upgrade: require('./upgrade.js'), 7 | }; 8 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | build: { 3 | "player1.html": "player1.html", 4 | "player2.html": "player2.html", 5 | "javascripts": "javascripts/", 6 | "css/": "css/", 7 | "imgs/": "imgs/" 8 | }, 9 | rpc: { 10 | host: "localhost", 11 | port: 8545, 12 | gas: 6000000 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /accounts-for-testing.txt: -------------------------------------------------------------------------------- 1 | Accounts for testing 2 | 3 | Player1: 4 | Seed: 'eager grow art loop sister system access know truly normal case moment' 5 | Account: 0x616d18096ce6e1038b5c3ded080cef8ab17b3843 6 | 7 | Player2: 8 | Seed: 'arrest trust sword select damage outer dignity thrive practice much chase velvet' 9 | Account: 0x2f33f148b5ff5f76e63460e14228a671923de628 10 | -------------------------------------------------------------------------------- /app/javascripts/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /support/UTC--2015-08-09T04-06-35.465758600Z--6a5b342ec71def8aac337b82969d9ddd811023c9: -------------------------------------------------------------------------------- 1 | {"address":"6a5b342ec71def8aac337b82969d9ddd811023c9","Crypto":{"cipher":"aes-128-ctr","ciphertext":"2537864929e3edc27758c2996bd04e216a0033691f09c9faf97cdf7e8d23176e","cipherparams":{"iv":"4b2af5f65ff741018d6d29b35f586c55"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"27e9802c6aa275b34e0ebcc249387bda85a50f02c05f149e83684c207fa10b2c"},"mac":"b36212eb449be62aff98529e9ae246c3a71cad0e853b3bf93d71eea52fb639f1"},"id":"8c938746-3581-4931-be7a-974b5e29bbb2","version":3} -------------------------------------------------------------------------------- /contracts/greeter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.2; 2 | contract mortal { 3 | /* Define variable owner of the type address*/ 4 | address owner; 5 | 6 | /* this function is executed at initialization and sets the owner of the contract */ 7 | function mortal() { owner = msg.sender; } 8 | 9 | /* Function to recover the funds on the contract */ 10 | function kill() { if (msg.sender == owner) selfdestruct(owner); } 11 | } 12 | 13 | contract greeter is mortal { 14 | /* define variable greeting of the type string */ 15 | string greeting; 16 | 17 | /* this runs when the contract is executed */ 18 | function greeter(string _greeting) public { 19 | greeting = _greeting; 20 | } 21 | 22 | /* main function */ 23 | function greet() constant returns (string) { 24 | return greeting; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /support/private_genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "nonce": "0x0000000000000042", 3 | "difficulty": "0x4", 4 | "config": { 5 | "chainId": 20, 6 | "homesteadBlock": 0, 7 | "eip155Block": 0, 8 | "eip158Block": 0 9 | }, 10 | "alloc": { 11 | "6a5b342ec71def8aac337b82969d9ddd811023c9": { 12 | "balance": "10000000000000000000000000" 13 | }, 14 | "0bd047561b8e280f1207a115feb2f0eaff11dc45": { 15 | "balance": "10000000000000000000000000" 16 | } 17 | }, 18 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 19 | "coinbase": "0x0000000000000000000000000000000000000000", 20 | "timestamp": "0x00", 21 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 22 | "extraData": "0x1100000000000000000000000000000000000000000000000000000000000000", 23 | "gasLimit": "0xffff1388" 24 | } 25 | -------------------------------------------------------------------------------- /app/javascripts/app.js: -------------------------------------------------------------------------------- 1 | var accounts; 2 | var account; 3 | 4 | function setStatus(message) { 5 | var status = document.getElementById("status"); 6 | status.innerHTML = message; 7 | }; 8 | 9 | function refreshBalance() { 10 | var meta = MetaCoin.deployed(); 11 | 12 | meta.getBalance.call(account, {from: account}).then(function(value) { 13 | var balance_element = document.getElementById("balance"); 14 | balance_element.innerHTML = value.valueOf(); 15 | }).catch(function(e) { 16 | console.log(e); 17 | setStatus("Error getting balance; see log."); 18 | }); 19 | }; 20 | 21 | function sendCoin() { 22 | var meta = MetaCoin.deployed(); 23 | 24 | var amount = parseInt(document.getElementById("amount").value); 25 | var receiver = document.getElementById("receiver").value; 26 | 27 | setStatus("Initiating transaction... (please wait)"); 28 | 29 | meta.sendCoin(receiver, amount, {from: account}).then(function() { 30 | setStatus("Transaction complete!"); 31 | refreshBalance(); 32 | }).catch(function(e) { 33 | console.log(e); 34 | setStatus("Error sending coin; see log."); 35 | }); 36 | }; 37 | 38 | window.onload = function() { 39 | web3.eth.getAccounts(function(err, accs) { 40 | if (err != null) { 41 | alert("There was an error fetching your accounts."); 42 | return; 43 | } 44 | 45 | if (accs.length == 0) { 46 | alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly."); 47 | return; 48 | } 49 | 50 | accounts = accs; 51 | account = accounts[0]; 52 | 53 | refreshBalance(); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /app/css/jumbotron-narrow.css: -------------------------------------------------------------------------------- 1 | /* Space out content a bit */ 2 | body { 3 | padding-top: 20px; 4 | padding-bottom: 20px; 5 | } 6 | 7 | /* Everything but the jumbotron gets side spacing for mobile first views */ 8 | .header, 9 | .marketing, 10 | .footer { 11 | padding-right: 15px; 12 | padding-left: 15px; 13 | } 14 | 15 | /* Custom page header */ 16 | .header { 17 | padding-bottom: 20px; 18 | border-bottom: 1px solid #e5e5e5; 19 | } 20 | /* Make the masthead heading the same height as the navigation */ 21 | .header h3 { 22 | margin-top: 0; 23 | margin-bottom: 0; 24 | line-height: 40px; 25 | } 26 | 27 | /* Custom page footer */ 28 | .footer { 29 | padding-top: 19px; 30 | color: #777; 31 | border-top: 1px solid #e5e5e5; 32 | } 33 | 34 | /* Customize container */ 35 | @media (min-width: 768px) { 36 | .container { 37 | max-width: 730px; 38 | } 39 | } 40 | .container-narrow > hr { 41 | margin: 30px 0; 42 | } 43 | 44 | /* Main marketing message and sign up button */ 45 | .jumbotron { 46 | text-align: center; 47 | border-bottom: 1px solid #e5e5e5; 48 | } 49 | .jumbotron .btn { 50 | padding: 14px 24px; 51 | font-size: 21px; 52 | } 53 | 54 | /* Supporting marketing content */ 55 | .marketing { 56 | margin: 40px 0; 57 | } 58 | .marketing p + h4 { 59 | margin-top: 28px; 60 | } 61 | 62 | /* Responsive: Portrait tablets and up */ 63 | @media screen and (min-width: 768px) { 64 | /* Remove the padding we set earlier */ 65 | .header, 66 | .marketing, 67 | .footer { 68 | padding-right: 0; 69 | padding-left: 0; 70 | } 71 | /* Space out the masthead */ 72 | .header { 73 | margin-bottom: 30px; 74 | } 75 | /* Remove the bottom border on the jumbotron for visual effect */ 76 | .jumbotron { 77 | border-bottom: 0; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/javascripts/signing.js: -------------------------------------------------------------------------------- 1 | 2 | var Transaction = require("ethereumjs-tx") 3 | var util = require("ethereumjs-util") 4 | 5 | var signTx = function (keystore, pwDerivedKey, rawTx, signingAddress, hdPathString) { 6 | 7 | if (hdPathString === undefined) { 8 | hdPathString = keystore.defaultHdPathString; 9 | } 10 | 11 | rawTx = util.stripHexPrefix(rawTx); 12 | signingAddress = util.stripHexPrefix(signingAddress); 13 | 14 | var txCopy = new Transaction(new Buffer(rawTx, 'hex')); 15 | 16 | var privKey = keystore.exportPrivateKey(signingAddress, pwDerivedKey, hdPathString); 17 | 18 | txCopy.sign(new Buffer(privKey, 'hex')); 19 | privKey = ''; 20 | 21 | return txCopy.serialize().toString('hex'); 22 | }; 23 | 24 | module.exports.signTx = signTx; 25 | 26 | var signMsg = function (keystore, pwDerivedKey, rawMsg, signingAddress, hdPathString) { 27 | 28 | if (hdPathString === undefined) { 29 | hdPathString = keystore.defaultHdPathString; 30 | } 31 | 32 | signingAddress = util.stripHexPrefix(signingAddress); 33 | 34 | var msgHash = util.sha3(rawMsg); 35 | 36 | var privKey = keystore.exportPrivateKey(signingAddress, pwDerivedKey, hdPathString); 37 | 38 | return util.ecsign(msgHash, new Buffer(privKey, 'hex')); 39 | }; 40 | 41 | module.exports.signMsg = signMsg; 42 | 43 | var recoverAddress = function (rawMsg, v, r, s) { 44 | 45 | var msgHash = util.sha3(rawMsg); 46 | 47 | return util.pubToAddress(util.ecrecover(msgHash, v, r, s)); 48 | }; 49 | 50 | module.exports.recoverAddress = recoverAddress; 51 | 52 | var concatSig = function (v, r, s) { 53 | r = util.fromSigned(r); 54 | s = util.fromSigned(s); 55 | v = util.bufferToInt(v); 56 | r = util.toUnsigned(r).toString('hex'); 57 | s = util.toUnsigned(s).toString('hex'); 58 | v = util.stripHexPrefix(util.intToHex(v)); 59 | return util.addHexPrefix(r.concat(s, v).toString("hex")); 60 | }; 61 | 62 | module.exports.concatSig = concatSig; 63 | -------------------------------------------------------------------------------- /app/javascripts/upgrade.js: -------------------------------------------------------------------------------- 1 | var CryptoJS = require('crypto-js'); 2 | var keystore = require('./keystore'); 3 | 4 | var Transaction = require('ethereumjs-tx'); 5 | var EC = require('elliptic').ec; 6 | var ec = new EC('secp256k1'); 7 | var bitcore = require('bitcore-lib'); 8 | var Random = bitcore.crypto.Random; 9 | var Hash = bitcore.crypto.Hash; 10 | var Mnemonic = require('bitcore-mnemonic'); 11 | var nacl = require('tweetnacl'); 12 | var scrypt = require('scrypt-async'); 13 | 14 | var legacyDecryptString = function (encryptedStr, password) { 15 | var decryptedStr = CryptoJS.AES.decrypt(encryptedStr.encStr, password, {'iv': encryptedStr.iv, 'salt': encryptedStr.salt }); 16 | return decryptedStr.toString(CryptoJS.enc.Latin1); 17 | }; 18 | 19 | var legacyGenerateEncKey = function(password, salt, keyHash) { 20 | var encKey = CryptoJS.PBKDF2(password, salt, { keySize: 512 / 32, iterations: 150 }).toString(); 21 | var hash = CryptoJS.SHA3(encKey).toString(); 22 | if (keyHash !== hash){ 23 | throw new Error('Invalid Password'); 24 | } 25 | return encKey; 26 | }; 27 | 28 | var upgradeOldSerialized = function (oldSerialized, password, callback) { 29 | 30 | // Upgrades old serialized version of the keystore 31 | // to the latest version 32 | var oldKS = JSON.parse(oldSerialized); 33 | 34 | if (oldKS.version === undefined || oldKS.version === 1) { 35 | 36 | var derivedKey = legacyGenerateEncKey(password, oldKS.salt, oldKS.keyHash); 37 | var seed = legacyDecryptString(oldKS.encSeed, derivedKey); 38 | keystore.deriveKeyFromPassword(password, function(err, pwDerivedKey) { 39 | var newKeyStore = new keystore(seed, pwDerivedKey); 40 | var hdIndex = oldKS.hdIndex; 41 | newKeyStore.generateNewAddress(pwDerivedKey, hdIndex); 42 | 43 | callback(null, newKeyStore.serialize()); 44 | }) 45 | } 46 | else { 47 | throw new Error('Keystore is not of correct version.') 48 | } 49 | } 50 | 51 | 52 | module.exports.upgradeOldSerialized = upgradeOldSerialized; 53 | -------------------------------------------------------------------------------- /migrations/1_deploy_pokemon.js: -------------------------------------------------------------------------------- 1 | account1Demo = '0x616d18096ce6e1038b5c3ded080cef8ab17b3843'; 2 | account2Demo = '0x2f33f148b5ff5f76e63460e14228a671923de628'; 3 | pokeCoinAddress = ''; 4 | pokeCentralAddress = ''; 5 | module.exports = function(deployer) { 6 | deployer.deploy(PokeCoin, {gas:2000000}).then(function(){ 7 | pokecoin = PokeCoin.deployed(); 8 | pokeCoinAddress = pokecoin.address; 9 | pokecoin.issueNew(10000, {gas:2000000}).then(function(){ 10 | return pokecoin.transfer(account1Demo, 5000, {from:web3.eth.accounts[0],gas:2000000}); 11 | }).then(function(){ 12 | return pokecoin.transfer(account2Demo, 5000, {from:web3.eth.accounts[0],gas:2000000}); 13 | }); 14 | 15 | 16 | }); 17 | deployer.deploy(PokeCentral, {gas:6000000}).then(function(){ 18 | pokecentral = PokeCentral.deployed(); 19 | pokeCentralAddress = pokecentral.address; 20 | pokecentral.newPokemon(3,500,40, {from:web3.eth.accounts[0],gas:6000000}).then(function(){ 21 | return pokecentral.transferPokemon(web3.eth.accounts[0], account1Demo, 1,{from:web3.eth.accounts[0],gas:6000000}); 22 | }); 23 | 24 | pokecentral.newPokemon(1,535,70, {from:web3.eth.accounts[0],gas:6000000}).then(function(){ 25 | return pokecentral.transferPokemon(web3.eth.accounts[0], account2Demo, 2,{from:web3.eth.accounts[0],gas:6000000}); 26 | }); 27 | 28 | pokecentral.newPokemon(4,546,80, {from:web3.eth.accounts[0],gas:6000000}).then(function(){ 29 | return pokecentral.transferPokemon(web3.eth.accounts[0], account2Demo, 3,{from:web3.eth.accounts[0],gas:6000000}); 30 | }); 31 | 32 | pokecentral.newPokemon(2,557,90, {from:web3.eth.accounts[0],gas:6000000}).then(function(){ 33 | return pokecentral.transferPokemon(web3.eth.accounts[0], account1Demo, 4,{from:web3.eth.accounts[0],gas:6000000}); 34 | }); 35 | 36 | 37 | }); 38 | deployer.deploy(PokeMarket, {gas:4000000}).then(function(){ 39 | pokemarket = PokeMarket.deployed(); 40 | return pokemarket.updatePokecoinAndPokemarketAddresses(pokeCoinAddress, pokeCentralAddress, {from:web3.eth.accounts[0],gas:4000000}).then(function(){ 41 | return pokecoin.updatePokeMarketAddress(pokemarket.address, {from:web3.eth.accounts[0],gas:4000000}).then(function(){ 42 | return pokecentral.updatePokeMarketAddress(pokemarket.address, {from:web3.eth.accounts[0],gas:4000000}); 43 | }); 44 | }); 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /test/mercado_pokemon-test.js: -------------------------------------------------------------------------------- 1 | contract('Mercado Pokemon', function(accounts) { 2 | var account1Demo = '0x616d18096ce6e1038b5c3ded080cef8ab17b3843'; 3 | var account2Demo = '0x2f33f148b5ff5f76e63460e14228a671923de628'; 4 | 5 | 6 | it("creating 10000 pokecoins", function(){ 7 | var pokecoin = PokeCoin.deployed(); 8 | return pokecoin.totalSupply().then(function(totalSupply) { 9 | assert.equal(totalSupply.toNumber(), 10000, "10000 pokecoins wasn't created"); 10 | }); 11 | }); 12 | 13 | it("should put 5000 pokecoins in the first account", function() { 14 | var pokecoin = PokeCoin.deployed(); 15 | return pokecoin.balanceOf(account1Demo).then(function(balance) { 16 | assert.equal(balance.toNumber(), 5000, "5000 wasn't in the first account"); 17 | }); 18 | }); 19 | 20 | it("should create 4 pokemons", function(){ 21 | var pokecentral = PokeCentral.deployed(); 22 | //pokecentral.newPokemon(3,500,40, {from:web3.eth.accounts[0],gas:2000000}); 23 | return pokecentral.totalPokemonSupply().then(function(totalSupply) { 24 | assert.equal(totalSupply.toNumber(), 4, "4 wasn't in the first account"); 25 | }); 26 | }); 27 | }); 28 | 29 | /*contract('PokeCentral', function(accounts){ 30 | var account1Demo = '0x616d18096ce6e1038b5c3ded080cef8ab17b3843'; 31 | var account2Demo = '0x2f33f148b5ff5f76e63460e14228a671923de628'; 32 | it("should create 4 pokemons", function(){ 33 | var pokecentral = PokeCentral.deployed(); 34 | //pokecentral.newPokemon(3,500,40, {from:web3.eth.accounts[0],gas:2000000}); 35 | return pokecentral.totalPokemonSupply().then(function(totalSupply) { 36 | assert.equal(totalSupply.toNumber(), 4, "4 wasn't in the first account"); 37 | }); 38 | }); 39 | 40 | it("should transfer a pokemon", function(){ 41 | var pokecentral = PokeCentral.deployed(); 42 | return pokecentral.pokemonToMaster(4).then(function(pokeMasterAddress) { 43 | assert.equal(pokeMasterAddress, account1Demo, "pokemon 4 not transferred to account1Demo"); 44 | }); 45 | }); 46 | 47 | }); 48 | 49 | contract('PokeMarket', function(accounts){ 50 | var account1Demo = '0x616d18096ce6e1038b5c3ded080cef8ab17b3843'; 51 | var account2Demo = '0x2f33f148b5ff5f76e63460e14228a671923de628'; 52 | 53 | it("should put a pokemon for selling", function(){ 54 | var pokemarket = PokeMarket.deployed(); 55 | 56 | return pokemarket.pokeSelling(1).then(function(status) { 57 | assert.equal(status, true, "pokemon 1 for selling"); 58 | }); 59 | }); 60 | 61 | }); 62 | */ 63 | -------------------------------------------------------------------------------- /app/player1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Mercado Pokémon 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 | 41 | 42 | 43 |
44 |
45 |

Mercado Pokémon

46 |

Workshop para desenvolvedores apresentando smart-contracts na plataforma Ethereum 47 |

48 | 49 |
50 |
51 |

Player1

52 |

Account address:

53 |

Seed:

54 | 55 |

Balance

56 |

57 | 58 |

Total Pokemons

59 |

60 | 61 |
62 |
63 | 64 |
65 | 66 | 67 |
68 |

Vender Pokémon

69 |
70 |
71 | 72 | 73 |
74 |
75 | 76 | 77 |
78 |
79 | 80 |
81 | 82 |
83 | 84 |

Mercado Ativo

85 |
86 |
87 |
88 | 89 | 90 | 91 |
92 | 93 |
94 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /app/player2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Mercado Pokémon 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 | 41 | 42 | 43 |
44 |
45 |

Mercado Pokémon

46 |

Workshop para desenvolvedores apresentando smart-contracts na plataforma Ethereum 47 |

48 | 49 |
50 |
51 |

Player2

52 |

Account address:

53 |

Seed:

54 | 55 |

Balance

56 |

57 | 58 |

Total Pokemons

59 |

60 | 61 |
62 |
63 | 64 |
65 | 66 | 67 |
68 |

Vender Pokémon

69 |
70 |
71 | 72 | 73 |
74 |
75 | 76 | 77 |
78 |
79 | 80 |
81 | 82 |
83 | 84 |

Mercado Ativo

85 |
86 |
87 |
88 | 89 | 90 | 91 |
92 | 93 |
94 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /app/javascripts/txutils.js: -------------------------------------------------------------------------------- 1 | var Transaction = require('ethereumjs-tx'); 2 | var coder = require('web3/lib/solidity/coder'); 3 | var rlp = require('rlp'); 4 | var CryptoJS = require('crypto-js'); 5 | 6 | function add0x (input) { 7 | if (typeof(input) !== 'string') { 8 | return input; 9 | } 10 | else if (input.length < 2 || input.slice(0,2) !== '0x') { 11 | return '0x' + input; 12 | } 13 | else { 14 | return input; 15 | } 16 | } 17 | 18 | function _encodeFunctionTxData (functionName, types, args) { 19 | 20 | var fullName = functionName + '(' + types.join() + ')'; 21 | var signature = CryptoJS.SHA3(fullName, { outputLength: 256 }).toString(CryptoJS.enc.Hex).slice(0, 8); 22 | var dataHex = signature + coder.encodeParams(types, args); 23 | 24 | return dataHex; 25 | } 26 | 27 | function _getTypesFromAbi (abi, functionName) { 28 | 29 | function matchesFunctionName(json) { 30 | return (json.name === functionName && json.type === 'function'); 31 | } 32 | 33 | function getTypes(json) { 34 | return json.type; 35 | } 36 | 37 | var funcJson = abi.filter(matchesFunctionName)[0]; 38 | 39 | return (funcJson.inputs).map(getTypes); 40 | } 41 | 42 | function functionTx (abi, functionName, args, txObject) { 43 | // txObject contains gasPrice, gasLimit, nonce, to, value 44 | 45 | var types = _getTypesFromAbi(abi, functionName); 46 | var txData = _encodeFunctionTxData(functionName, types, args); 47 | 48 | var txObjectCopy = {}; 49 | txObjectCopy.to = add0x(txObject.to); 50 | txObjectCopy.gasPrice = add0x(txObject.gasPrice); 51 | txObjectCopy.gasLimit = add0x(txObject.gasLimit); 52 | txObjectCopy.nonce = add0x(txObject.nonce); 53 | txObjectCopy.data = add0x(txData); 54 | txObjectCopy.value = add0x(txObject.value); 55 | 56 | return (new Transaction(txObjectCopy)).serialize().toString('hex'); 57 | } 58 | 59 | function createdContractAddress (fromAddress, nonce) { 60 | var rlpEncodedHex = rlp.encode([new Buffer(fromAddress, 'hex'), nonce]).toString('hex'); 61 | var rlpEncodedWordArray = CryptoJS.enc.Hex.parse(rlpEncodedHex); 62 | var hash = CryptoJS.SHA3(rlpEncodedWordArray, {outputLength: 256}).toString(CryptoJS.enc.Hex); 63 | 64 | return hash.slice(24); 65 | } 66 | 67 | function createContractTx (fromAddress, txObject) { 68 | // txObject contains gasPrice, gasLimit, value, data, nonce 69 | 70 | var txObjectCopy = {}; 71 | txObjectCopy.to = add0x(txObject.to); 72 | txObjectCopy.gasPrice = add0x(txObject.gasPrice); 73 | txObjectCopy.gasLimit = add0x(txObject.gasLimit); 74 | txObjectCopy.nonce = add0x(txObject.nonce); 75 | txObjectCopy.data = add0x(txObject.data); 76 | txObjectCopy.value = add0x(txObject.value); 77 | 78 | var contractAddress = createdContractAddress(fromAddress, txObject.nonce); 79 | var tx = new Transaction(txObjectCopy); 80 | 81 | return {tx: tx.serialize().toString('hex'), addr: contractAddress}; 82 | } 83 | 84 | function valueTx (txObject) { 85 | // txObject contains gasPrice, gasLimit, value, nonce 86 | 87 | var txObjectCopy = {}; 88 | txObjectCopy.to = add0x(txObject.to); 89 | txObjectCopy.gasPrice = add0x(txObject.gasPrice); 90 | txObjectCopy.gasLimit = add0x(txObject.gasLimit); 91 | txObjectCopy.nonce = add0x(txObject.nonce); 92 | txObjectCopy.value = add0x(txObject.value); 93 | 94 | var tx = new Transaction(txObjectCopy); 95 | 96 | return tx.serialize().toString('hex'); 97 | } 98 | 99 | module.exports = { 100 | _encodeFunctionTxData: _encodeFunctionTxData, 101 | _getTypesFromAbi: _getTypesFromAbi, 102 | functionTx: functionTx, 103 | createdContractAddress: createdContractAddress, 104 | createContractTx: createContractTx, 105 | valueTx: valueTx 106 | }; 107 | -------------------------------------------------------------------------------- /contracts/PokeCoin.sol: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edilson Osorio Junior - OriginalMy.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | pragma solidity ^0.4.2; 24 | contract tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData); } 25 | 26 | contract accessControlled { 27 | address public owner; 28 | address public pokeMarketAddress; 29 | 30 | function owned() { 31 | owner = msg.sender; 32 | } 33 | 34 | modifier onlyOwner { 35 | if (msg.sender != owner) throw; 36 | /* o caracter "_" é substituído pelo corpo da funcao onde o modifier é utilizado */ 37 | _; 38 | } 39 | 40 | function transferOwnership(address newOwner) onlyOwner { 41 | owner = newOwner; 42 | } 43 | 44 | function updatePokeMarketAddress(address marketAddress) onlyOwner { 45 | pokeMarketAddress = marketAddress; 46 | } 47 | 48 | } 49 | 50 | contract PokeCoin is accessControlled{ 51 | /* Variaveis publicas */ 52 | string public standard = 'Pokecoin 0.1'; 53 | string public name = 'Pokecoin'; 54 | string public symbol = "pkc"; 55 | uint256 public totalSupply; 56 | 57 | /* Cria array com os balancos */ 58 | mapping (address => uint256) public balanceOf; 59 | 60 | /* Gera um evento publico padrao, que ira notificar os clientes */ 61 | event Transfer(address indexed from, address indexed to, uint256 value); 62 | 63 | /* Inicializa o contrato com o numero inicial de tokens */ 64 | function PokeCoin( uint256 initialSupply, address account1Demo, address account2Demo) { 65 | owner = msg.sender; 66 | 67 | /* Envia as pokecoins para o criador */ 68 | balanceOf[msg.sender] = initialSupply; 69 | /* Ajusta o totalsupply */ 70 | totalSupply = initialSupply; 71 | 72 | /* Se incluiu os enderecos demo, então divide as pokecoins entre eles */ 73 | if (account1Demo != 0 && account2Demo != 0){ 74 | transfer(account1Demo, totalSupply/2); 75 | transfer(account2Demo, totalSupply/2); 76 | } 77 | } 78 | 79 | /* Transferencia de pokecoins (necessario para Mist fazer a transferencia) */ 80 | function transfer(address _to, uint256 _value) onlyOwner { 81 | if (balanceOf[msg.sender] < _value) throw; // Verifica se o remetente possui pokecoins suficientes 82 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Checa se nao houve ataque 83 | balanceOf[msg.sender] -= _value; // Subtrai as pokecoins do remetente 84 | balanceOf[_to] += _value; // Adiciona pokecoins para o destinatario 85 | Transfer(msg.sender, _to, _value); // Notifica os clientes que estiverem nonitorando 86 | } 87 | 88 | /* Emite novas pokecoins para o owner distribuí-las */ 89 | function issueNew(uint256 newSupply) onlyOwner{ 90 | balanceOf[msg.sender] += newSupply; 91 | totalSupply += newSupply; 92 | } 93 | 94 | /* Exclui pokecoins do endereço do owner */ 95 | function vanishCoins(uint256 qtdCoinsToDelete) onlyOwner{ 96 | if (balanceOf[msg.sender] < qtdCoinsToDelete) throw; // Verifica se o owner possui pokecoins suficientes para exclusao 97 | balanceOf[msg.sender] -= qtdCoinsToDelete; 98 | totalSupply -= qtdCoinsToDelete; 99 | } 100 | 101 | /* Funcao para os contratos poderem transferir pokecoins */ 102 | function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { 103 | if (msg.sender != owner && msg.sender != pokeMarketAddress) throw; // Somente da acesso ao owner e ao mercado pokemon para executar essa funcao 104 | 105 | if (balanceOf[_from] < _value) throw; // Verifica se o remetente possui pokecoins suficientes 106 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Checa se nao houve ataque 107 | balanceOf[_from] -= _value; // Subtrai as pokecoins do remetente 108 | balanceOf[_to] += _value; // Adiciona pokecoins para o destinatario 109 | Transfer(_from, _to, _value); // Notifica os clientes que estiverem nonitorando 110 | return true; 111 | } 112 | 113 | /* Uma funcao sem nome '()' eh chamada todas as vezes que forem enviados ethers para ela */ 114 | function () { 115 | throw; // Nao permite o recebimento de ether 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/javascripts/encryption.js: -------------------------------------------------------------------------------- 1 | var util = require("ethereumjs-util"); 2 | var nacl = require('tweetnacl'); 3 | 4 | function nacl_encodeHex(msgUInt8Arr) { 5 | var msgBase64 = nacl.util.encodeBase64(msgUInt8Arr); 6 | return (new Buffer(msgBase64, 'base64')).toString('hex'); 7 | } 8 | 9 | function nacl_decodeHex(msgHex) { 10 | var msgBase64 = (new Buffer(msgHex, 'hex')).toString('base64'); 11 | return nacl.util.decodeBase64(msgBase64); 12 | } 13 | 14 | function _asymEncryptRaw (keystore, pwDerivedKey, msgUint8Array, myPubKey, theirPubKey, hdPathString) { 15 | 16 | if (hdPathString === undefined) { 17 | hdPathString = keystore.defaultHdPathString; 18 | } 19 | 20 | if (keystore.ksData[hdPathString].info.purpose !== 'asymEncrypt') { 21 | throw new Error('encryption._asymEncryptRaw: Function not defined when purpose is not "asymEncrypt"'); 22 | } 23 | 24 | if (keystore.ksData[hdPathString].encPrivKeys[myPubKey] === undefined) { 25 | throw new Error('encryption._asymEncryptRaw: public key not found in KeyStore'); 26 | } 27 | 28 | var privKey = keystore.exportPrivateKey(myPubKey, pwDerivedKey, hdPathString); 29 | var privKeyUInt8Array = nacl_decodeHex(privKey); 30 | var pubKeyUInt8Array = nacl_decodeHex(theirPubKey); 31 | var nonce = nacl.randomBytes(nacl.box.nonceLength); 32 | var encryptedMessage = nacl.box(msgUint8Array, nonce, pubKeyUInt8Array, privKeyUInt8Array); 33 | 34 | var output = { 35 | alg: 'curve25519-xsalsa20-poly1305', 36 | nonce: nacl.util.encodeBase64(nonce), 37 | ciphertext: nacl.util.encodeBase64(encryptedMessage) 38 | }; 39 | 40 | return output; 41 | } 42 | 43 | function _asymDecryptRaw (keystore, pwDerivedKey, encMsg, theirPubKey, myPubKey, hdPathString) { 44 | 45 | if (hdPathString === undefined) { 46 | hdPathString = keystore.defaultHdPathString; 47 | } 48 | 49 | if (keystore.ksData[hdPathString].info.purpose !== 'asymEncrypt') { 50 | throw new Error('encryption._asymDecryptRaw: Function not defined when purpose is not "asymEncrypt"'); 51 | } 52 | 53 | if (keystore.ksData[hdPathString].encPrivKeys[myPubKey] === undefined) { 54 | throw new Error('encryption._asymDecryptRaw: public key not found in KeyStore'); 55 | } 56 | 57 | var privKey = keystore.exportPrivateKey(myPubKey, pwDerivedKey, hdPathString); 58 | var privKeyUInt8Array = nacl_decodeHex(privKey); 59 | var pubKeyUInt8Array = nacl_decodeHex(theirPubKey); 60 | 61 | var nonce = nacl.util.decodeBase64(encMsg.nonce); 62 | var ciphertext = nacl.util.decodeBase64(encMsg.ciphertext); 63 | var cleartext = nacl.box.open(ciphertext, nonce, pubKeyUInt8Array, privKeyUInt8Array); 64 | 65 | return cleartext; 66 | 67 | } 68 | 69 | var asymEncryptString = function (keystore, pwDerivedKey, msg, myPubKey, theirPubKey, hdPathString) { 70 | 71 | var messageUInt8Array = nacl.util.decodeUTF8(msg); 72 | 73 | return _asymEncryptRaw(keystore, pwDerivedKey, messageUInt8Array, myPubKey, theirPubKey, hdPathString); 74 | 75 | } 76 | 77 | var asymDecryptString = function (keystore, pwDerivedKey, encMsg, theirPubKey, myPubKey, hdPathString) { 78 | 79 | var cleartext = _asymDecryptRaw(keystore, pwDerivedKey, encMsg, theirPubKey, myPubKey, hdPathString); 80 | 81 | if (cleartext === false) { 82 | return false; 83 | } 84 | else { 85 | return nacl.util.encodeUTF8(cleartext); 86 | } 87 | 88 | } 89 | 90 | var multiEncryptString = function (keystore, pwDerivedKey, msg, myPubKey, theirPubKeyArray, hdPathString) { 91 | 92 | var messageUInt8Array = nacl.util.decodeUTF8(msg); 93 | var symEncryptionKey = nacl.randomBytes(nacl.secretbox.keyLength); 94 | var symNonce = nacl.randomBytes(nacl.secretbox.nonceLength); 95 | 96 | var symEncMessage = nacl.secretbox(messageUInt8Array, symNonce, symEncryptionKey); 97 | 98 | if (theirPubKeyArray.length < 1) { 99 | throw new Error('Found no pubkeys to encrypt to.'); 100 | } 101 | 102 | var encryptedSymKey = {}; 103 | encryptedSymKey = [] 104 | for (var i=0; i/support/UTC--2015-08-09T04-06-35.465758600Z--6a5b342ec71def8aac337b82969d9ddd811023c9 private_net/keystore 30 | ``` 31 | 32 | 3. Start the blockchain with a new genesis 33 | ``` 34 | geth --datadir ./private_net init /support/private_genesis.json 35 | ``` 36 | 37 | 4. Start the node 38 | ``` 39 | geth --fast --cache 512 -ipcpath ~/Library/Ethereum/geth.ipc --networkid 1234 --datadir ./private_net --unlock 0 --rpc --rpccorsdomain="*" --rpcaddr "0.0.0.0" 40 | ``` 41 | 5. Unlock the account[0] with the password 'abcd' 42 | 43 | 6. On a new terminal, attach to the geth console 44 | ``` 45 | geth attach 46 | miner.start(1) 47 | ``` 48 | 49 | ## Setup using Ethereum Wallet (for using Truffle, jump to the next section) 50 | 51 | 1. Open file `accounts-for-testing.txt` 52 | 53 | 2. Load `contracts/pokecoin.sol` in Ethereum Wallet (contracts > Deploy new contract) and use the demo account addresses from Player1 and Player2 to launch the contract. (2000000 gas) 54 | 55 | 3. Load `contracts/pokecentral.sol` in Ethereum Wallet and use the demo account addresses from Player1 and Player2 to launch the contract. (6000000 gas) 56 | 57 | 4. Write the `Pokecoin` and `PokeCentral` addresses on a notepad. 58 | 59 | 5. Load `contracts/pokemarket.sol` no Ethereum Wallet and use `PokeCoinAddress` and `PokeCentralAddress` to launch the contract. (6000000 gas) 60 | 61 | 6. Write the `Pokemarket` address on a notepad. 62 | 63 | 7. You must update `PokeMarketAddress` in the contracts `PokeCoin` e `PokeCentral` to enable access. 64 | 65 | 8. Update the variables `PokeCoinAddress`, `PokeCentralAddress` e `PokeMarketAddress` in `javascripts/mercadopokemon.js` 66 | 67 | 9. Load `app/player1.html` and `app/player2.html` in separate windows on Chrome browser. 68 | 69 | 70 | ## Setup truffle 71 | 1. Go to the truffle-mercado-pokemon dir and open the Truffle console on a new terminal 72 | ``` 73 | truffle console 74 | ``` 75 | 76 | 2. Run the migrate to load contracts to blockchain 77 | ``` 78 | migrate --reset 79 | ``` 80 | 81 | 3. Update vars PokeCoinAddress, PokeCentralAddress and PokeMarketAddress in mercadopokemon.js 82 | 83 | ## Using Mercado Pokemon 84 | 85 | Open player1.html and player2.html each one in a new window in Chrome 86 | 87 | ## Examples using Truffle console 88 | 89 | 1. Transfer pokecoins to the players 1 and 2 using the console: 90 | ``` 91 | pokecoin.transfer(account1Demo, 5000, {from:web3.eth.accounts[0],gas:2000000}); 92 | pokecoin.transfer(account2Demo, 5000, {from:web3.eth.accounts[0],gas:2000000}); 93 | ``` 94 | 95 | 5.1 Verify is the transfers were suceeded 96 | ``` 97 | pokecoin.balanceOf(account1Demo) // 5000 98 | pokecoin.balanceOf(account2Demo) // 5000 99 | ``` 100 | 101 | 102 | 2. Create the next four valid pokemons: 103 | ``` 104 | pokecentral.newPokemon(3,500,40, {from:web3.eth.accounts[0],gas:2000000}); 105 | pokecentral.newPokemon(1,535,70, {from:web3.eth.accounts[0],gas:2000000}); 106 | pokecentral.newPokemon(4,546,80, {from:web3.eth.accounts[0],gas:2000000}); 107 | pokecentral.newPokemon(2,557,90, {from:web3.eth.accounts[0],gas:2000000}); 108 | ``` 109 | 110 | 2.1 Verify pokemon total 111 | ``` 112 | pokecentral.totalPokemonSupply() // 4 113 | ``` 114 | 115 | 2.2 Verify the pokemon owner (wait until mining) 116 | ``` 117 | pokecentral.pokemons(0); 118 | pokecentral.pokemons(1); 119 | pokecentral.pokemons(2); 120 | pokecentral.pokemons(3); 121 | pokecentral.pokemons(4); 122 | ``` 123 | 124 | 3. Transfer the pokemons to player1 and player2 125 | ``` 126 | pokecentral.transferPokemon(web3.eth.accounts[0], account1Demo, 1,{from:web3.eth.accounts[0],gas:2000000}); 127 | pokecentral.transferPokemon(web3.eth.accounts[0], account1Demo, 4,{from:web3.eth.accounts[0],gas:2000000}); 128 | pokecentral.transferPokemon(web3.eth.accounts[0], account2Demo, 2,{from:web3.eth.accounts[0],gas:2000000}); 129 | pokecentral.transferPokemon(web3.eth.accounts[0], account2Demo, 3,{from:web3.eth.accounts[0],gas:2000000}); 130 | ``` 131 | 132 | 3.1 Verify the pokemon owner, as in 2.2 133 | 134 | 4. Update the PokeMarketAddress in dapps PokeCoin e PokeCentral 135 | ``` 136 | pokecoin.updatePokeMarketAddress(pokemarket.address, {from:web3.eth.accounts[0],gas:2000000}); 137 | pokecentral.updatePokeMarketAddress(pokemarket.address, {from:web3.eth.accounts[0],gas:2000000}); 138 | ``` 139 | 140 | 4.1 Verify the addresses 141 | ``` 142 | pokecoin.pokeMarketAddress(); 143 | pokecentral.pokeMarketAddress(); 144 | ``` 145 | 146 | 5. Put the pokemon 1 from account1Demo for selling by 2000 pkc: 147 | ``` 148 | pokemarket.newSale(account1Demo, 1, 2000, {from:web3.eth.accounts[0],gas:2000000}); 149 | ``` 150 | 151 | 6. Verify the number of active sells: 152 | ``` 153 | pokemarket.totalActiveSales(); 154 | ``` 155 | 156 | 7. Verify the active sell 1 data: 157 | ``` 158 | pokemarket.pokeSales(0); 159 | ``` 160 | 161 | 8. Verify if the pokemon 1 is active for selling: 162 | ``` 163 | pokemarket.pokeSelling(1); 164 | ``` 165 | 166 | 9. Buy the pokemon 1, with the player 2 167 | ``` 168 | pokemarket.buyPokemon(account2Demo, 1, {from:web3.eth.accounts[0],gas:2000000}); 169 | ``` 170 | 171 | 10. Verify if the pokemon owner was changed to account2Demo address 172 | ``` 173 | pokecentral.pokemons(1); 174 | ``` 175 | 176 | 11. Verify the pokecoins total for each player 177 | ``` 178 | pokecoin.balanceOf(account1Demo); 179 | pokecoin.balanceOf(account2Demo); 180 |    ``` 181 | 182 | ## How it works: 183 | 184 | Once the contracts were loaded, we created the PokeCoins and Pokemons, and distributed them between Player1 and Player2 accounts. 185 | Through the html files you can put Pokemons for sale and make the purchase. 186 | 187 | ## Obs: 188 | It is necessary that the account [0] is unlocked and has funds. To do this, type: personal.unlockAccount(eth.accounts[0]) on the console. 189 | 190 | If pokecoins are transferred to an account, you can trade them directly in the secondary market, but that wallet must have funds for paying the rsk gas transaction. 191 | -------------------------------------------------------------------------------- /app/javascripts/hooked-web3-provider.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | "use strict"; 4 | 5 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 6 | 7 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 8 | 9 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 10 | 11 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 12 | 13 | var factory = function factory(web3) { 14 | var HookedWeb3Provider = (function (_web3$providers$HttpProvider) { 15 | _inherits(HookedWeb3Provider, _web3$providers$HttpProvider); 16 | 17 | function HookedWeb3Provider(_ref) { 18 | var host = _ref.host; 19 | var transaction_signer = _ref.transaction_signer; 20 | 21 | _classCallCheck(this, HookedWeb3Provider); 22 | 23 | _get(Object.getPrototypeOf(HookedWeb3Provider.prototype), "constructor", this).call(this, host); 24 | this.transaction_signer = transaction_signer; 25 | 26 | // Cache of the most up to date transaction counts (nonces) for each address 27 | // encountered by the web3 provider that's managed by the transaction signer. 28 | this.global_nonces = {}; 29 | } 30 | 31 | // We can't support *all* synchronous methods because we have to call out to 32 | // a transaction signer. So removing the ability to serve any. 33 | 34 | _createClass(HookedWeb3Provider, [{ 35 | key: "send", 36 | value: function send(payload, callback) { 37 | var _this = this; 38 | 39 | var requests = payload; 40 | if (!(requests instanceof Array)) { 41 | requests = [requests]; 42 | } 43 | 44 | var _iteratorNormalCompletion = true; 45 | var _didIteratorError = false; 46 | var _iteratorError = undefined; 47 | 48 | try { 49 | for (var _iterator = requests[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 50 | var request = _step.value; 51 | 52 | if (request.method == "eth_sendTransaction") { 53 | throw new Error("HookedWeb3Provider does not support synchronous transactions. Please provide a callback."); 54 | } 55 | } 56 | } catch (err) { 57 | _didIteratorError = true; 58 | _iteratorError = err; 59 | } finally { 60 | try { 61 | if (!_iteratorNormalCompletion && _iterator["return"]) { 62 | _iterator["return"](); 63 | } 64 | } finally { 65 | if (_didIteratorError) { 66 | throw _iteratorError; 67 | } 68 | } 69 | } 70 | 71 | var finishedWithRewrite = function finishedWithRewrite() { 72 | return _get(Object.getPrototypeOf(HookedWeb3Provider.prototype), "send", _this).call(_this, payload, callback); 73 | }; 74 | 75 | return this.rewritePayloads(0, requests, {}, finishedWithRewrite); 76 | } 77 | 78 | // Catch the requests at the sendAsync level, rewriting all sendTransaction 79 | // methods to sendRawTransaction, calling out to the transaction_signer to 80 | // get the data for sendRawTransaction. 81 | }, { 82 | key: "sendAsync", 83 | value: function sendAsync(payload, callback) { 84 | var _this2 = this; 85 | 86 | var finishedWithRewrite = function finishedWithRewrite() { 87 | _get(Object.getPrototypeOf(HookedWeb3Provider.prototype), "sendAsync", _this2).call(_this2, payload, callback); 88 | }; 89 | 90 | var requests = payload; 91 | 92 | if (!(payload instanceof Array)) { 93 | requests = [payload]; 94 | } 95 | 96 | this.rewritePayloads(0, requests, {}, finishedWithRewrite); 97 | } 98 | 99 | // Rewrite all eth_sendTransaction payloads in the requests array. 100 | // This takes care of batch requests, and updates the nonces accordingly. 101 | }, { 102 | key: "rewritePayloads", 103 | value: function rewritePayloads(index, requests, session_nonces, finished) { 104 | var _this3 = this; 105 | 106 | if (index >= requests.length) { 107 | return finished(); 108 | } 109 | 110 | var payload = requests[index]; 111 | 112 | // Function to remove code duplication for going to the next payload 113 | var next = function next(err) { 114 | if (err != null) { 115 | return finished(err); 116 | } 117 | return _this3.rewritePayloads(index + 1, requests, session_nonces, finished); 118 | }; 119 | 120 | // If this isn't a transaction we can modify, ignore it. 121 | if (payload.method != "eth_sendTransaction") { 122 | return next(); 123 | } 124 | 125 | var tx_params = payload.params[0]; 126 | var sender = tx_params.from; 127 | 128 | this.transaction_signer.hasAddress(sender, function (err, has_address) { 129 | if (err != null || has_address == false) { 130 | return next(err); 131 | } 132 | 133 | // Get the nonce, requesting from web3 if we haven't already requested it in this session. 134 | // Remember: "session_nonces" is the nonces we know about for this batch of rewriting (this "session"). 135 | // Having this cache makes it so we only need to call getTransactionCount once per batch. 136 | // "global_nonces" is nonces across the life of this provider. 137 | var getNonce = function getNonce(done) { 138 | // If a nonce is specified in our nonce list, use that nonce. 139 | var nonce = session_nonces[sender]; 140 | if (nonce != null) { 141 | done(null, nonce); 142 | } else { 143 | // Include pending transactions, so the nonce is set accordingly. 144 | // Note: "pending" doesn't seem to take effect for some Ethereum clients (geth), 145 | // hence the need for global_nonces. 146 | // We call directly to our own sendAsync method, because the web3 provider 147 | // is not guaranteed to be set. 148 | _this3.sendAsync({ 149 | jsonrpc: '2.0', 150 | method: 'eth_getTransactionCount', 151 | params: [sender, "pending"], 152 | id: new Date().getTime() 153 | }, function (err, result) { 154 | if (err != null) { 155 | done(err); 156 | } else { 157 | var new_nonce = result.result; 158 | done(null, web3.toDecimal(new_nonce)); 159 | } 160 | }); 161 | } 162 | }; 163 | 164 | // Get the nonce, requesting from web3 if we need to. 165 | // We then store the nonce and update it so we don't have to 166 | // to request from web3 again. 167 | getNonce(function (err, nonce) { 168 | if (err != null) { 169 | return finished(err); 170 | } 171 | 172 | // Set the expected nonce, and update our caches of nonces. 173 | // Note that if our session nonce is lower than what we have cached 174 | // across all transactions (and not just this batch) use our cached 175 | // version instead, even if 176 | var final_nonce = Math.max(nonce, _this3.global_nonces[sender] || 0); 177 | 178 | // Update the transaction parameters. 179 | tx_params.nonce = web3.toHex(final_nonce); 180 | 181 | // Update caches. 182 | session_nonces[sender] = final_nonce + 1; 183 | _this3.global_nonces[sender] = final_nonce + 1; 184 | 185 | // If our transaction signer does represent the address, 186 | // sign the transaction ourself and rewrite the payload. 187 | _this3.transaction_signer.signTransaction(tx_params, function (err, raw_tx) { 188 | if (err != null) { 189 | return next(err); 190 | } 191 | 192 | payload.method = "eth_sendRawTransaction"; 193 | payload.params = [raw_tx]; 194 | return next(); 195 | }); 196 | }); 197 | }); 198 | } 199 | }]); 200 | 201 | return HookedWeb3Provider; 202 | })(web3.providers.HttpProvider); 203 | 204 | return HookedWeb3Provider; 205 | }; 206 | 207 | if (typeof module !== 'undefined') { 208 | module.exports = factory(require("web3")); 209 | } else { 210 | var web3 = new Web3(); 211 | window.HookedWeb3Provider = factory(web3); 212 | } 213 | -------------------------------------------------------------------------------- /contracts/PokeMarket.sol: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edilson Osorio Junior - OriginalMy.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* Prototipagem dos contratos que serao chamados a partir deste */ 24 | pragma solidity ^0.4.2; 25 | contract pokeCoinContract { mapping (address => uint256) public balanceOf; function transferFrom(address _from, address _to, uint256 _value){ } } 26 | contract pokeCentralContract { mapping (uint256 => address) public pokemonToMaster; function transferPokemon(address _from, address _to, uint256 _pokemonID) { } } 27 | 28 | contract accessControlled { 29 | address public owner; 30 | 31 | function owned() { 32 | owner = msg.sender; 33 | } 34 | 35 | modifier onlyOwner { 36 | if (msg.sender != owner) throw; 37 | /* o caracter "_" é substituído pelo corpo da funcao onde o modifier é utilizado */ 38 | _; 39 | } 40 | 41 | function transferOwnership(address newOwner) onlyOwner { 42 | owner = newOwner; 43 | } 44 | 45 | 46 | } 47 | 48 | contract PokeMarket is accessControlled { 49 | pokeCoinContract public pokeCoin; 50 | pokeCentralContract public pokeCentral; 51 | 52 | uint public totalPokemonSales; 53 | uint public totalActiveSales; 54 | 55 | PokeSale[] public pokeSales; 56 | 57 | mapping (uint => bool) public pokeSelling; 58 | mapping (address => uint[]) public pokeMasterSelling; 59 | mapping (uint => uint) public pokeSaleIndex; 60 | 61 | struct PokeSale{ 62 | address pokeSeller; 63 | address pokeBuyer; 64 | uint pokeID; 65 | uint pokePrice; 66 | bool pokeSold; 67 | bool pokeSellActive; 68 | } 69 | 70 | event NewSale(address pokeSellerAddress, uint pokemonID, uint pokemonSalePrice); 71 | event StopSale(address pokeSellerAddress, uint pokemonID); 72 | event PokeTrade(address pokeBuyerAddress, address pokeSellerAddress, uint pokemonID ); 73 | 74 | /* Inicializa o mercado pokemon apontando os endereços da PokeCoin e da PokeCentral */ 75 | function PokeMarket(pokeCoinContract pokeCoinAddress, pokeCentralContract pokeCentralAddress) { 76 | owner = msg.sender; 77 | pokeCoin = pokeCoinContract(pokeCoinAddress); 78 | pokeCentral = pokeCentralContract(pokeCentralAddress); 79 | } 80 | 81 | 82 | /* Inicia uma nova venda */ 83 | function newSale(address pokeSellerAddress, uint pokemonID, uint pokemonSalePrice) onlyOwner returns (bool success){ 84 | if (pokeSellerAddress != pokeCentral.pokemonToMaster(pokemonID)) throw; // Verifica se o vendedor possui o pokemon colocado a venda 85 | if (pokeSelling[pokemonID]) throw; // Verifica se ja ha venda ativa para este pokemon 86 | 87 | uint pokeSalesID = pokeSales.length++; 88 | PokeSale p = pokeSales[pokeSalesID]; 89 | if (p.pokeSellActive) throw; 90 | p.pokeSeller = pokeSellerAddress; 91 | p.pokeID = pokemonID; 92 | p.pokePrice = pokemonSalePrice; 93 | p.pokeSold = false; 94 | p.pokeSellActive = true; 95 | 96 | pokeSelling[pokemonID] = true; 97 | pokeSaleIndex[pokemonID] = pokeSalesID; // Em muitos casos é importante criar um indice para o pesquisar no struct 98 | 99 | //addPokemonToSellingList(pokeSellerAddress, pokemonID); // Adiciona esta venda na lista de vendas 100 | 101 | totalPokemonSales+=1; 102 | totalActiveSales+=1; 103 | 104 | NewSale(pokeSellerAddress, pokemonID, pokemonSalePrice); // Notifica os clientes que há uma nova venda 105 | return (true); 106 | } 107 | 108 | /* Cancela uma venda ativa */ 109 | function stopSale(address pokeSellerAddress, uint pokemonID) onlyOwner { 110 | if (msg.sender != owner && msg.sender != pokeSellerAddress) throw; // Verifica se quem está solicitando o cancelamento da venda é o criador da mesma ou o owner 111 | if (pokeSellerAddress != pokeCentral.pokemonToMaster(pokemonID)) throw; // Verifica se o pokemon é do proprietario 112 | if (!pokeSelling[pokemonID]) throw; // Verifica se a venda esta ativa 113 | 114 | uint pokeSalesID = pokeSaleIndex[pokemonID]; 115 | PokeSale p = pokeSales[pokeSalesID]; 116 | if (!p.pokeSellActive) throw; 117 | p.pokeSellActive = false; 118 | pokeSelling[pokemonID] = false; 119 | 120 | delPokemonFromSellingList(pokeSellerAddress, pokemonID); 121 | 122 | totalActiveSales-=1; 123 | 124 | StopSale(pokeSellerAddress, pokemonID); 125 | } 126 | 127 | /* Compra um Pokemon */ 128 | function buyPokemon(address pokeBuyerAddress, uint pokemonID) { 129 | if (pokeBuyerAddress == pokeCentral.pokemonToMaster(pokemonID)) throw; // Verifica se quem está comprando é o próprio vendedor 130 | if (!pokeSelling[pokemonID]) throw; // Verifica se o pokemon esta a venda 131 | 132 | uint pokeSalesID = pokeSaleIndex[pokemonID]; 133 | PokeSale p = pokeSales[pokeSalesID]; 134 | if (!p.pokeSellActive) throw; // Verifica se na struct o pokemon esta com venda ativa 135 | if (pokeCoin.balanceOf(pokeBuyerAddress) < p.pokePrice) throw; // Verifica se o comprador possui fundos suficientes para comprar o pokemon 136 | 137 | pokeCoin.transferFrom(pokeBuyerAddress, p.pokeSeller, p.pokePrice); // Chama a funcao transferFrom do contrato pokecoin 138 | pokeCentral.transferPokemon(p.pokeSeller, pokeBuyerAddress, pokemonID); // Chama a funcao transferPokemon do contrato pokecentral 139 | p.pokeBuyer = pokeBuyerAddress; // Ajusta o endereço do comprador 140 | p.pokeSold = true; // Marca o pokemon como vendido 141 | 142 | stopSale(pokeBuyerAddress,pokemonID); // Cancela a venda 143 | 144 | PokeTrade(pokeBuyerAddress, p.pokeSeller, pokemonID ); // Notifica os clientes que a venda ocorreu 145 | 146 | } 147 | 148 | /* Adiciona o pokemon para a lista de vendas */ 149 | function addPokemonToSellingList(address pokeSellerAddress, uint pokemonID) onlyOwner internal { 150 | uint[] tempList = pokeMasterSelling[pokeSellerAddress]; // Carrega a lista de vendas para o vendedor 151 | tempList[tempList.length++] = pokemonID; // Adiciona um pokemon ao final da lista 152 | 153 | pokeMasterSelling[pokeSellerAddress] = cleanArray(tempList); // Substitui o mapping que possui a lista de vendas, reorganizando o array (retirando os zeros) 154 | } 155 | 156 | 157 | /* Exclui um pokemon da lista de vendas */ 158 | function delPokemonFromSellingList(address pokeSellerAddress, uint pokemonID) onlyOwner internal { 159 | uint[] tempList = pokeMasterSelling[pokeSellerAddress]; // Carrega a lista de vendas para o vendedor 160 | uint count = tempList.length; // Conta o numero de itens da lista 161 | 162 | for (uint i=0; i 0 ){ 182 | tempList[j] = pokeList[i]; // Ajusta a lista temporaria com os valores que nao sao zero 183 | j++; 184 | } 185 | } 186 | uint[] memory tempList2 = new uint[](j); // Cria uma segunda lista em memória, com tamanho do "j" que e a contagem dos itens do "for" anterior 187 | for (i=0; i< j; i++) tempList2[i] = tempList[i]; // Adiciona cada item do primeiro array para o segundo array. O excedente fica de fora 188 | return tempList2; // Retorna a segunda lista 189 | } 190 | 191 | /* Uma funcao sem nome '()' eh chamada todas as vezes que forem enviados ethers para ela */ 192 | function (){ 193 | throw; // Nao permite o recebimento de ether 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /contracts/PokeCentral.sol: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edilson Osorio Junior - OriginalMy.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | pragma solidity ^0.4.2; 24 | contract accessControlled { 25 | address public owner; 26 | address public pokeMarketAddress; 27 | 28 | function accessControlled() { 29 | owner = msg.sender; 30 | } 31 | 32 | modifier onlyOwner { 33 | if (msg.sender != owner) throw; 34 | /* o caracter "_" é substituído pelo corpo da funcao onde o modifier é utilizado */ 35 | _; 36 | } 37 | 38 | function transferOwnership(address newOwner) onlyOwner { 39 | owner = newOwner; 40 | } 41 | 42 | function updatePokeMarketAddress(address marketAddress) onlyOwner { 43 | pokeMarketAddress = marketAddress; 44 | } 45 | 46 | } 47 | 48 | 49 | contract PokeCentral is accessControlled { 50 | 51 | uint256 public totalPokemonSupply; 52 | 53 | Pokemon[] public pokemons; 54 | PokemonMaster[] public pokeMasters; 55 | 56 | mapping (uint256 => address) public pokemonToMaster; 57 | mapping (address => uint256) public pokeOwnerIndex; 58 | mapping (address => uint256) public totalPokemonsFromMaster; 59 | mapping (address => uint256[]) public balanceOf; 60 | 61 | struct Pokemon { 62 | uint pokeNumber; 63 | string pokeName; 64 | string pokeType; 65 | uint pokeCP; 66 | uint pokeHP; 67 | bytes32 pokemonHash; 68 | address pokeOwner; 69 | } 70 | 71 | struct PokemonMaster { 72 | address pokeMaster; 73 | uint[] pokemons; 74 | } 75 | 76 | /* Exemplo de array bidimensional para armazenar o nome do pokemon e seu tipo. O índice é seu número na Pokedex */ 77 | string[][] public pokemonNameTypes = [["Pokemon 0", "invalid"], ["Bulbassauro", "Grass/Poison"], ["Charmander", "Fire"], ["Squirtle","Water"], ["Pikachu","Eletric"]]; // Este tipo não aceita bytes 78 | 79 | /* Gera um evento publico no blockchain e avisa os clientes que estao monitorando */ 80 | event Transfer(address from, address to, uint256 value); 81 | event CreatePokemon(uint id, string name, uint cp, uint hp ); 82 | event UpdatePokemon(uint id, string name, uint cp, uint hp ); 83 | event UpdateMasterPokemons(address owner, uint total); 84 | event Log1(uint number); 85 | event Log2(string message); 86 | 87 | /* Inicializa o contrato */ 88 | function PokeCentral(address account1Demo, address account2Demo) { 89 | owner = msg.sender; 90 | 91 | newPokemonMaster(owner); // Todos pokemons serao criados para este owner 92 | 93 | /* 94 | Há um problema com um array de indices, pois quando um item é excluído, é substituído por 0. 95 | Então vamos criar um pokemon fake no primeiro item e ajustar a quantidade para ignorá-lo 96 | */ 97 | newPokemon(0,0,0); // Pokemon Índice 0 98 | totalPokemonSupply-=1; // Ajusta o total de pokemons porque o primeiro é fake 99 | 100 | /* Criacao de pokemons iniciais */ 101 | //newPokemon(3,500,40); 102 | //newPokemon(1,535,70); 103 | //newPokemon(4,546,80); 104 | //newPokemon(2,557,90); 105 | 106 | /* Se as contas demo forem apresentadas no carregamento, então os pokemons criados serão distribuidos entre elas */ 107 | //if (account1Demo != 0 && account2Demo != 0){ 108 | //transferPokemon(msg.sender, account1Demo, 1); 109 | //transferPokemon(msg.sender, account1Demo, 4); 110 | //transferPokemon(msg.sender, account2Demo, 2); 111 | //transferPokemon(msg.sender, account2Demo, 3); 112 | //} 113 | } 114 | 115 | /* Criar novo Pokemon */ 116 | function newPokemon(uint pokemonNumber, uint cp, uint hp ) onlyOwner returns (bool success) { // cp e hp podem ser fornecidos randomicamente por https://api.random.org/json-rpc/1/basic 117 | uint pokemonID = pokemons.length++; 118 | Pokemon p = pokemons[pokemonID]; 119 | p.pokeNumber = pokemonNumber; 120 | p.pokeName = pokemonNameTypes[pokemonNumber][0]; 121 | p.pokeType = pokemonNameTypes[pokemonNumber][1]; 122 | p.pokeCP = cp; 123 | p.pokeHP = hp; 124 | 125 | p.pokemonHash = sha3(p.pokeNumber,p.pokeName,p.pokeType,p.pokeCP,p.pokeHP); // Hash de verificacao das infos do pokémon 126 | p.pokeOwner = owner; 127 | pokemonToMaster[pokemonID] = owner; 128 | addPokemonToMaster(owner, pokemonID); 129 | 130 | totalPokemonSupply += 1; 131 | CreatePokemon(pokemonID, p.pokeName, p.pokeCP, p.pokeHP); 132 | return true; 133 | } 134 | 135 | /* Alterar CP e HP de um Pokemon */ 136 | function updatePokemon(uint _pokemonID, uint _cp, uint _hp ) onlyOwner returns (bool success) { 137 | Pokemon p = pokemons[_pokemonID]; 138 | p.pokeCP = _cp; 139 | p.pokeHP = _hp; 140 | 141 | p.pokemonHash = sha3(p.pokeNumber,p.pokeName,p.pokeType,p.pokeCP,p.pokeHP); 142 | 143 | UpdatePokemon(_pokemonID, p.pokeName, p.pokeCP, p.pokeHP); 144 | return true; 145 | 146 | } 147 | 148 | /* Criar novo Mestre Pokemon */ 149 | function newPokemonMaster(address pokemonMaster) onlyOwner returns (bool success) { 150 | uint ownerID = pokeMasters.length++; 151 | PokemonMaster o = pokeMasters[ownerID]; 152 | o.pokeMaster = pokemonMaster; 153 | pokeOwnerIndex[pokemonMaster] = ownerID; 154 | return true; 155 | } 156 | 157 | /* Transferencia de Pokemons */ 158 | function transferPokemon(address _from, address _to, uint256 _pokemonID) returns (uint pokemonID, address from, address to) { 159 | if (msg.sender != owner && msg.sender != pokeMarketAddress) throw; 160 | Pokemon p = pokemons[_pokemonID]; 161 | if (p.pokeOwner != _from) throw; 162 | /* Se o Mestre Pokémon não existe ainda, crie-o */ 163 | if (pokeOwnerIndex[_to] == 0 && _to != pokemonToMaster[0] ) newPokemonMaster(_to); 164 | p.pokeOwner = _to; 165 | pokemonToMaster[_pokemonID] = _to; 166 | delPokemonFromMaster(_from, _pokemonID); 167 | addPokemonToMaster(_to, _pokemonID); 168 | Transfer(_from, _to, _pokemonID); 169 | return (_pokemonID, _from, _to); 170 | } 171 | 172 | /* Vincula um pokemon ao seu treinador */ 173 | function addPokemonToMaster(address _pokemonOwner, uint256 _pokemonID) internal returns (address pokeOwner, uint[] pokemons, uint pokemonsTotal) { 174 | if (msg.sender != owner && msg.sender != pokeMarketAddress) throw; 175 | 176 | uint ownerID = pokeOwnerIndex[_pokemonOwner]; 177 | PokemonMaster o = pokeMasters[ownerID]; 178 | uint[] pokeList = o.pokemons; 179 | 180 | /* usando array.push ele adiciona 0,_pokemonID no array */ 181 | // o.pokemons.push(_pokemonID); 182 | 183 | /* 184 | Ao invés de simplesmente adicionar um pokemon ao final da lista, verifica se há slot zerado no array, senao adiciona ao final. 185 | O blockchain não apaga itens, o uso de 'delete array[x]' substitui o item por 0. 186 | Ex: 187 | array[] = [ 1, 2, 3, 4 ] 188 | delete array[3]; 189 | array[] = [ 1, 2, 0, 4 ] 190 | */ 191 | 192 | bool slot; 193 | for (uint i=0; i < pokeList.length; i++){ 194 | if (pokeList[i] == 0){ 195 | slot = true; 196 | break; 197 | } 198 | } 199 | if (slot == true){ 200 | o.pokemons[i] = _pokemonID; 201 | } else { 202 | uint j = pokeList.length++; 203 | o.pokemons[j] = _pokemonID; 204 | } 205 | balanceOf[_pokemonOwner] = cleanArray(o.pokemons); 206 | 207 | qtdePokemons(_pokemonOwner); 208 | UpdateMasterPokemons(_pokemonOwner, o.pokemons.length); 209 | return (_pokemonOwner, o.pokemons, o.pokemons.length); 210 | } 211 | 212 | /* Desvincula um pokemon do seu treinador */ 213 | function delPokemonFromMaster(address _pokemonOwner, uint256 _pokemonID) internal returns (address pokeOwner, uint[] pokemons, uint pokemonsTotal) { 214 | if (msg.sender != owner && msg.sender != pokeMarketAddress) throw; 215 | 216 | uint ownerID = pokeOwnerIndex[_pokemonOwner]; 217 | PokemonMaster o = pokeMasters[ownerID]; 218 | uint[] pokeList = o.pokemons; 219 | 220 | for (uint i=0; i < pokeList.length; i++){ 221 | if (pokeList[i] == _pokemonID){ 222 | delete pokeList[i]; 223 | } 224 | } 225 | 226 | // http://ethereum.stackexchange.com/questions/3373/how-to-clear-large-arrays-without-blowing-the-gas-limit 227 | o.pokemons=cleanArray(pokeList); // Rearranja o array, eliminando os itens zerados, a custo de gas 228 | 229 | balanceOf[_pokemonOwner] = cleanArray(o.pokemons); 230 | 231 | qtdePokemons(_pokemonOwner); 232 | UpdateMasterPokemons(_pokemonOwner, o.pokemons.length); 233 | return (_pokemonOwner, o.pokemons, o.pokemons.length); 234 | } 235 | 236 | /* Funcao ilustrativa: lista pokemons de um treinador pois o browser solidity não mostra o conteúdo dos arrays no struct e no mapping */ 237 | function listPokemons( address _pokeOwner ) returns (address, uint[]){ 238 | uint ownerID = pokeOwnerIndex[_pokeOwner]; 239 | PokemonMaster o = pokeMasters[ownerID]; 240 | 241 | /* Lista pokemons tanto do struct quanto do mapping. */ 242 | return ( _pokeOwner, balanceOf[_pokeOwner] ); 243 | } 244 | 245 | /* Conta a qtde de pokemons em um array que possui zeros */ 246 | function qtdePokemons( address _pokeOwner) internal returns (uint qtde){ 247 | if (msg.sender != owner && msg.sender != pokeMarketAddress) throw; 248 | 249 | uint ownerID = pokeOwnerIndex[_pokeOwner]; 250 | PokemonMaster o = pokeMasters[ownerID]; 251 | uint[] pokeList = o.pokemons; 252 | uint count = 0; 253 | for (uint i=0; i < pokeList.length; i++){ 254 | if ( pokeList[i] > 0 ){ 255 | count++; 256 | } 257 | } 258 | totalPokemonsFromMaster[_pokeOwner] = count; 259 | return count; 260 | } 261 | 262 | /* Exemplo 2: Conta a qtde de pokemons diretamente do mapping */ 263 | function qtdePokemonsMapping( address _pokeOwner) internal returns (uint qtde){ 264 | uint[] tempList = balanceOf[_pokeOwner]; 265 | totalPokemonsFromMaster[_pokeOwner] = tempList.length; 266 | return tempList.length; 267 | } 268 | 269 | 270 | /* Esta funcao elimina todos os itens com zero do array, ao custo de gas */ 271 | function cleanArray(uint[] pokeList) internal returns (uint[]){ 272 | uint[] memory tempArray = new uint[](pokeList.length); 273 | uint j = 0; 274 | for (uint i=0; i < pokeList.length; i++){ 275 | if ( pokeList[i] > 0 ){ 276 | tempArray[j] = pokeList[i]; 277 | j++; 278 | } 279 | } 280 | uint[] memory tempArray2 = new uint[](j); 281 | for (i=0; i< j; i++) tempArray2[i] = tempArray[i]; 282 | return tempArray2; 283 | } 284 | 285 | /* Se tentarem enviar ether para o end desse contrato, ele rejeita */ 286 | function () { 287 | throw; 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /app/javascripts/mercadopokemon.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edilson Osorio Junior - OriginalMy.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | var Web3 = require('web3'); 24 | var web3 = new Web3(); 25 | var eth = web3.eth; 26 | 27 | var txutils = lightwallet.txutils; 28 | var signing = lightwallet.signing; 29 | var encryption = lightwallet.encryption; 30 | 31 | /* ABI dos três Dapps */ 32 | var abiPokeCoin = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"marketAddress","type":"address"}],"name":"updatePokeMarketAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"standard","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"pokeMarketAddress","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"qtdCoinsToDelete","type":"uint256"}],"name":"vanishCoins","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"newSupply","type":"uint256"}],"name":"issueNew","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"owned","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"type":"function"},{"inputs":[{"name":"initialSupply","type":"uint256"},{"name":"account1Demo","type":"address"},{"name":"account2Demo","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]; 33 | var abiPokeCentral = [{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"marketAddress","type":"address"}],"name":"updatePokeMarketAddress","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"totalPokemonSupply","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"pokeOwnerIndex","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"pokeMasters","outputs":[{"name":"pokeMaster","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"pokeMarketAddress","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"pokemonToMaster","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"pokemons","outputs":[{"name":"pokeNumber","type":"uint256"},{"name":"pokeName","type":"string"},{"name":"pokeType","type":"string"},{"name":"pokeCP","type":"uint256"},{"name":"pokeHP","type":"uint256"},{"name":"pokemonHash","type":"bytes32"},{"name":"pokeOwner","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_pokemonID","type":"uint256"},{"name":"_cp","type":"uint256"},{"name":"_hp","type":"uint256"}],"name":"updatePokemon","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"pokemonNumber","type":"uint256"},{"name":"cp","type":"uint256"},{"name":"hp","type":"uint256"}],"name":"newPokemon","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_pokemonID","type":"uint256"}],"name":"transferPokemon","outputs":[{"name":"pokemonID","type":"uint256"},{"name":"from","type":"address"},{"name":"to","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"pokemonNameTypes","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"pokemonMaster","type":"address"}],"name":"newPokemonMaster","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[],"name":"owned","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_pokeOwner","type":"address"}],"name":"listPokemons","outputs":[{"name":"","type":"address"},{"name":"","type":"uint256[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"totalPokemonsFromMaster","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"account1Demo","type":"address"},{"name":"account2Demo","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"cp","type":"uint256"},{"indexed":false,"name":"hp","type":"uint256"}],"name":"CreatePokemon","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"cp","type":"uint256"},{"indexed":false,"name":"hp","type":"uint256"}],"name":"UpdatePokemon","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"total","type":"uint256"}],"name":"UpdateMasterPokemons","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"number","type":"uint256"}],"name":"Log1","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"message","type":"string"}],"name":"Log2","type":"event"}]; 34 | var abiPokeMarket = [{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"pokeSales","outputs":[{"name":"pokeSeller","type":"address"},{"name":"pokeBuyer","type":"address"},{"name":"pokeID","type":"uint256"},{"name":"pokePrice","type":"uint256"},{"name":"pokeSold","type":"bool"},{"name":"pokeSellActive","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"newPokecoinAddress","type":"address"},{"name":"newPokecentralAddress","type":"address"}],"name":"updatePokecoinAndPokemarketAddresses","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"pokeSaleIndex","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"pokeSelling","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"pokeSellerAddress","type":"address"},{"name":"pokemonID","type":"uint256"},{"name":"pokemonSalePrice","type":"uint256"}],"name":"newSale","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"totalPokemonSales","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"pokeCoin","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"pokeSellerAddress","type":"address"},{"name":"pokemonID","type":"uint256"}],"name":"stopSale","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"pokeCentral","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"pokeBuyerAddress","type":"address"},{"name":"pokemonID","type":"uint256"}],"name":"buyPokemon","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"totalActiveSales","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"owned","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"pokeMasterSelling","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"type":"function"},{"inputs":[{"name":"pokeCoinAddress","type":"address"},{"name":"pokeCentralAddress","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pokeSellerAddress","type":"address"},{"indexed":false,"name":"pokemonID","type":"uint256"},{"indexed":false,"name":"pokemonSalePrice","type":"uint256"}],"name":"NewSale","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pokeSellerAddress","type":"address"},{"indexed":false,"name":"pokemonID","type":"uint256"}],"name":"StopSale","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pokeBuyerAddress","type":"address"},{"indexed":false,"name":"pokeSellerAddress","type":"address"},{"indexed":false,"name":"pokemonID","type":"uint256"}],"name":"PokeTrade","type":"event"}]; 35 | 36 | /* Endereços dos Dapps */ 37 | var pokeCoinAddress = '0x2ba25711fa1f94998d875bf80acbc2cccdf9abbb'; 38 | var pokeCentralAddress = '0xe46a592b722ead6cec7e0eb736deb59dbb325cab'; 39 | var pokeMarketAddress = '0x6a25a406758bd91766191c39a3d94a429c98dd11'; 40 | 41 | /* Carregamento dos Contratos */ 42 | var MyPokeCoinContract = web3.eth.contract(abiPokeCoin); 43 | var pokeCoin = MyPokeCoinContract.at(pokeCoinAddress); 44 | 45 | var MyPokeCentralContract = web3.eth.contract(abiPokeCentral); 46 | var pokeCentral = MyPokeCentralContract.at(pokeCentralAddress); 47 | 48 | var MyPokeMarketContract = web3.eth.contract(abiPokeMarket); 49 | var pokeMarket = MyPokeMarketContract.at(pokeMarketAddress); 50 | 51 | var keystore; 52 | 53 | var accountAddress = "xxx"; 54 | var password = 'mercadopokemon'; 55 | 56 | /* Inicializa conexão com o node local */ 57 | web3.setProvider(new web3.providers.HttpProvider()); 58 | 59 | function setWeb3Provider(keystore) { 60 | var web3Provider = new HookedWeb3Provider({ 61 | host: "http://localhost:8545", 62 | transaction_signer: keystore 63 | }); 64 | 65 | web3.setProvider(web3Provider); 66 | }; 67 | 68 | /* Criação/carregamento do account */ 69 | lightwallet.keystore.deriveKeyFromPassword(password, function(err, pwDerivedKey) { 70 | 71 | /* Se for criar uma nova conta com um novo seed, descomente as duas linhas abaixo */ 72 | 73 | //var extraEntropy = 'mercadopokemon129387612348917236'; 74 | //seed = lightwallet.keystore.generateRandomSeed(extraEntropy); 75 | 76 | keystore = new lightwallet.keystore(seed, pwDerivedKey); 77 | keystore.generateNewAddress(pwDerivedKey, 1); 78 | 79 | accountAddress = keystore.getAddresses()[0]; 80 | 81 | document.getElementById("accountAddress").innerText = '0x'+accountAddress; 82 | document.getElementById("seed").innerText = seed; 83 | 84 | web3.eth.defaultAccount = eth.accounts[0]; 85 | 86 | }); 87 | 88 | function showStatus(){ 89 | var currentBalance = pokeCoin.balanceOf('0x'+accountAddress).toNumber(); 90 | var qtdePokemons = pokeCentral.totalPokemonsFromMaster('0x'+accountAddress); 91 | document.getElementById("currbalance").innerText = 'Balance: ' + currentBalance + ' pkc'; 92 | document.getElementById("totalPokemons").innerText = 'Qtde : ' + qtdePokemons + ' pokemon(s)'; 93 | 94 | 95 | /* Constrói informações sobre cada Pokémon da conta */ 96 | var html = ''; 97 | html += '' 98 | for (i=0; i' 102 | for (j=1; j<5; j++){ 103 | html += ''; 104 | } 105 | html += ''; 106 | } 107 | html+='
IDPokemonTipoCPHP
' + pokeCentral.pokemons(pokeID)[j] + '
' 108 | 109 | document.getElementById("pokemonList").innerHTML = html; 110 | 111 | /* Constrói as vendas ativas */ 112 | var html2 = ''; 113 | html2 = '' 114 | html2 += ''; 115 | for (i=0;;i++){ 116 | if (pokeMarket.pokeSales(i)[0] == '0x') break; 117 | if (pokeMarket.pokeSales(i)[5]){ 118 | html2 += ''; 119 | saleID = pokeMarket.pokeSales(i)[2].toNumber(); 120 | html2 += '' 121 | for (j=1; j<5; j++){ 122 | html2 += ''; 123 | } 124 | html2 += ''; 125 | html2 += ''; 126 | if (pokeMarket.pokeSales(i)[0] == '0x'+accountAddress){ 127 | html2 += ''; 128 | } else { 129 | html2 += ''; 130 | } 131 | html2 += ''; 132 | } 133 | } 134 | html2 += '
IDPokemonTipoCPHPValorDono#
' + saleID + '' + pokeCentral.pokemons(saleID)[j] + '' + pokeMarket.pokeSales(i)[3].toNumber() + ' pkc' + pokeMarket.pokeSales(i)[0].substring(0,6) + '...'+ '
' 135 | document.getElementById("pokeSells").innerHTML = html2; 136 | }; 137 | 138 | function setSell(){ 139 | var pokeIDSell = document.getElementById("pokeIDSell").value; 140 | var pokePriceSell = document.getElementById("pokePriceSell").value; 141 | pokeMarket.newSale('0x'+accountAddress, pokeIDSell, pokePriceSell, {value: 0, gas: 290654, gasPrice: 20000000000}, function(err, hash) { 142 | if (!err){ 143 | console.log("Transacao enviada: " + hash); 144 | document.getElementById("msgVenda").innerText = hash; 145 | } else { 146 | console.log("Erro no envio: " + err); 147 | document.getElementById("msgVenda").innerText = err; 148 | }; 149 | }); 150 | var pokeIDSell = document.getElementById("pokeIDSell").value = ''; 151 | var pokePriceSell = document.getElementById("pokePriceSell").value = ''; 152 | } 153 | 154 | function setBuy(pokemonID){ 155 | var pokeIDBuy = pokemonID; 156 | pokeMarket.buyPokemon('0x'+accountAddress, pokeIDBuy, {value: 0, gas: 428638, gasPrice: 20000000000}, function(err, hash) { 157 | if (!err){ 158 | console.log("Transacao enviada: " + hash); 159 | document.getElementById("msgMercado").innerText = hash; 160 | } else { 161 | console.log("Erro no envio: " + err); 162 | document.getElementById("msgMercado").innerText = err; 163 | }; 164 | }); 165 | pokeIDBuy = ''; 166 | } 167 | 168 | function stopSell(pokemonID){ 169 | pokeMarket.stopSale('0x'+accountAddress, pokemonID, {value: 0, gas: 428638, gasPrice: 20000000000}, function(err, hash) { 170 | if (!err){ 171 | console.log("Transacao enviada: " + hash); 172 | document.getElementById("msgMercado").innerText = hash; 173 | } else { 174 | console.log("Erro no envio: " + err); 175 | document.getElementById("msgMercado").innerText = err; 176 | }; 177 | }); 178 | } 179 | 180 | 181 | /* Filtro do web3 para atualizar os valores automaticamente */ 182 | var filter = web3.eth.filter('latest'); 183 | filter.watch(function(error, result) { 184 | if (!error) { 185 | showStatus() 186 | } 187 | }); 188 | -------------------------------------------------------------------------------- /app/javascripts/keystore.js: -------------------------------------------------------------------------------- 1 | var CryptoJS = require('crypto-js'); 2 | var Transaction = require('ethereumjs-tx'); 3 | var EC = require('elliptic').ec; 4 | var ec = new EC('secp256k1'); 5 | var bitcore = require('bitcore-lib'); 6 | var Random = bitcore.crypto.Random; 7 | var Hash = bitcore.crypto.Hash; 8 | var Mnemonic = require('bitcore-mnemonic'); 9 | var nacl = require('tweetnacl'); 10 | var scrypt = require('scrypt-async'); 11 | 12 | var encryption = require('./encryption'); 13 | var signing = require('./signing'); 14 | 15 | function strip0x (input) { 16 | if (typeof(input) !== 'string') { 17 | return input; 18 | } 19 | else if (input.length >= 2 && input.slice(0,2) === '0x') { 20 | return input.slice(2); 21 | } 22 | else { 23 | return input; 24 | } 25 | } 26 | 27 | function add0x (input) { 28 | if (typeof(input) !== 'string') { 29 | return input; 30 | } 31 | else if (input.length < 2 || input.slice(0,2) !== '0x') { 32 | return '0x' + input; 33 | } 34 | else { 35 | return input; 36 | } 37 | } 38 | 39 | function leftPadString (stringToPad, padChar, length) { 40 | 41 | var repreatedPadChar = ''; 42 | for (var i=0; i 32) { 239 | throw new Error('Private key larger than 32 bytes. Aborting!'); 240 | } 241 | 242 | var encPrivKey = KeyStore._encryptKey(privkeyHex, pwDerivedKey); 243 | keys[i] = { 244 | privKey: privkeyHex, 245 | encPrivKey: encPrivKey 246 | } 247 | } 248 | 249 | return keys; 250 | }; 251 | 252 | 253 | // This function is tested using the test vectors here: 254 | // http://www.di-mgt.com.au/sha_testvectors.html 255 | KeyStore._concatAndSha256 = function(entropyBuf0, entropyBuf1) { 256 | 257 | var totalEnt = Buffer.concat([entropyBuf0, entropyBuf1]); 258 | if (totalEnt.length !== entropyBuf0.length + entropyBuf1.length) { 259 | throw new Error('generateRandomSeed: Logic error! Concatenation of entropy sources failed.') 260 | } 261 | 262 | var hashedEnt = Hash.sha256(totalEnt); 263 | 264 | return hashedEnt; 265 | } 266 | 267 | // External static functions 268 | 269 | 270 | // Generates a random seed. If the optional string 271 | // extraEntropy is set, a random set of entropy 272 | // is created, then concatenated with extraEntropy 273 | // and hashed to produce the entropy that gives the seed. 274 | // Thus if extraEntropy comes from a high-entropy source 275 | // (like dice) it can give some protection from a bad RNG. 276 | // If extraEntropy is not set, the random number generator 277 | // is used directly. 278 | 279 | KeyStore.generateRandomSeed = function(extraEntropy) { 280 | 281 | var seed = ''; 282 | if (extraEntropy === undefined) { 283 | seed = new Mnemonic(Mnemonic.Words.ENGLISH); 284 | } 285 | else if (typeof extraEntropy === 'string') { 286 | var entBuf = new Buffer(extraEntropy); 287 | var randBuf = Random.getRandomBuffer(256 / 8); 288 | var hashedEnt = this._concatAndSha256(randBuf, entBuf).slice(0, 128 / 8); 289 | seed = new Mnemonic(hashedEnt, Mnemonic.Words.ENGLISH); 290 | } 291 | else { 292 | throw new Error('generateRandomSeed: extraEntropy is set but not a string.') 293 | } 294 | 295 | return seed.toString(); 296 | }; 297 | 298 | KeyStore.isSeedValid = function(seed) { 299 | return Mnemonic.isValid(seed, Mnemonic.Words.ENGLISH) 300 | }; 301 | 302 | KeyStore.prototype.isDerivedKeyCorrect = function(pwDerivedKey) { 303 | 304 | var paddedSeed = KeyStore._decryptString(this.encSeed, pwDerivedKey); 305 | if (paddedSeed.length > 0) { 306 | return true; 307 | } 308 | 309 | return false; 310 | 311 | }; 312 | 313 | // Takes keystore serialized as string and returns an instance of KeyStore 314 | KeyStore.deserialize = function (keystore) { 315 | var jsonKS = JSON.parse(keystore); 316 | 317 | if (jsonKS.version === undefined || jsonKS.version === 1) { 318 | throw new Error('Old version of serialized keystore. Please use KeyStore.upgradeOldSerialized() to convert it to the latest version.') 319 | } 320 | 321 | // Create keystore 322 | var keystoreX = new KeyStore(); 323 | 324 | keystoreX.encSeed = jsonKS.encSeed; 325 | keystoreX.encHdRootPriv = jsonKS.encHdRootPriv; 326 | keystoreX.ksData = jsonKS.ksData; 327 | 328 | return keystoreX; 329 | }; 330 | 331 | // External API functions 332 | 333 | KeyStore.prototype.serialize = function () { 334 | var jsonKS = {'encSeed': this.encSeed, 335 | 'ksData' : this.ksData, 336 | 'encHdRootPriv' : this.encHdRootPriv, 337 | 'version' : this.version}; 338 | 339 | return JSON.stringify(jsonKS); 340 | }; 341 | 342 | KeyStore.prototype.getAddresses = function (hdPathString) { 343 | 344 | if (hdPathString === undefined) { 345 | hdPathString = this.defaultHdPathString; 346 | } 347 | 348 | if (this.ksData[hdPathString].info.purpose !== 'sign') { 349 | throw new Error('KeyStore.getAddresses: Addresses not defined when purpose is not "sign"'); 350 | } 351 | 352 | return this.ksData[hdPathString].addresses; 353 | 354 | }; 355 | 356 | KeyStore.prototype.getSeed = function (pwDerivedKey) { 357 | var paddedSeed = KeyStore._decryptString(this.encSeed, pwDerivedKey); 358 | return paddedSeed.trim(); 359 | }; 360 | 361 | KeyStore.prototype.exportPrivateKey = function (address, pwDerivedKey, hdPathString) { 362 | 363 | if (hdPathString === undefined) { 364 | hdPathString = this.defaultHdPathString; 365 | } 366 | 367 | var address = strip0x(address); 368 | if (this.ksData[hdPathString].encPrivKeys[address] === undefined) { 369 | throw new Error('KeyStore.exportPrivateKey: Address not found in KeyStore'); 370 | } 371 | 372 | var encPrivKey = this.ksData[hdPathString].encPrivKeys[address]; 373 | var privKey = KeyStore._decryptKey(encPrivKey, pwDerivedKey); 374 | 375 | return privKey; 376 | }; 377 | 378 | KeyStore.prototype.generateNewAddress = function(pwDerivedKey, n, hdPathString) { 379 | 380 | if (hdPathString === undefined) { 381 | hdPathString = this.defaultHdPathString; 382 | } 383 | 384 | if (this.ksData[hdPathString].info.purpose !== 'sign') { 385 | throw new Error('KeyStore.generateNewAddress: Address not defined when purpose is not "sign"'); 386 | } 387 | 388 | if (!this.encSeed) { 389 | throw new Error('KeyStore.generateNewAddress: No seed set'); 390 | } 391 | n = n || 1; 392 | var keys = this._generatePrivKeys(pwDerivedKey, n, hdPathString); 393 | 394 | for (var i = 0; i < n; i++) { 395 | var keyObj = keys[i]; 396 | var address = KeyStore._computeAddressFromPrivKey(keyObj.privKey); 397 | this.ksData[hdPathString].encPrivKeys[address] = keyObj.encPrivKey; 398 | this.ksData[hdPathString].addresses.push(address); 399 | } 400 | 401 | }; 402 | 403 | KeyStore.prototype.generateNewEncryptionKeys = function(pwDerivedKey, n, hdPathString) { 404 | 405 | if (hdPathString === undefined) { 406 | hdPathString = this.defaultHdPathString; 407 | } 408 | 409 | if (this.ksData[hdPathString].info.purpose !== 'asymEncrypt') { 410 | throw new Error('KeyStore.generateNewEncryptionKeys: Address not defined when purpose is not "asymEncrypt"'); 411 | } 412 | 413 | if (!this.encSeed) { 414 | throw new Error('KeyStore.generateNewEncryptionKeys: No seed set'); 415 | } 416 | n = n || 1; 417 | var keys = this._generatePrivKeys(pwDerivedKey, n, hdPathString); 418 | 419 | var curve = this.ksData[hdPathString].info.curve; 420 | for (var i = 0; i < n; i++) { 421 | var keyObj = keys[i]; 422 | var pubkey = KeyStore._computePubkeyFromPrivKey(keyObj.privKey, curve); 423 | this.ksData[hdPathString].encPrivKeys[pubkey] = keyObj.encPrivKey; 424 | this.ksData[hdPathString].pubKeys.push(pubkey); 425 | } 426 | 427 | }; 428 | 429 | KeyStore.prototype.getPubKeys = function (hdPathString) { 430 | 431 | if (hdPathString === undefined) { 432 | hdPathString = this.defaultHdPathString; 433 | } 434 | 435 | if (this.ksData[hdPathString].info.purpose !== 'asymEncrypt') { 436 | throw new Error('KeyStore.getPubKeys: Not defined when purpose is not "asymEncrypt"'); 437 | } 438 | 439 | if (this.ksData[hdPathString].pubKeys === undefined) { 440 | throw new Error('KeyStore.getPubKeys: No pubKeys data found!'); 441 | } 442 | 443 | return this.ksData[hdPathString].pubKeys; 444 | } 445 | 446 | KeyStore.deriveKeyFromPassword = function(password, callback) { 447 | 448 | var salt = 'lightwalletSalt'; // should we have user-defined salt? 449 | var logN = 14; 450 | var r = 8; 451 | var dkLen = 32; 452 | var interruptStep = 200; 453 | 454 | var cb = function(derKey) { 455 | try{ 456 | var ui8arr = (new Uint8Array(derKey)); 457 | callback(null, ui8arr); 458 | } catch (err) { 459 | callback(err); 460 | } 461 | } 462 | 463 | scrypt(password, salt, logN, r, dkLen, interruptStep, cb, null); 464 | } 465 | 466 | 467 | 468 | // Async functions exposed for Hooked Web3-provider 469 | // hasAddress(address, callback) 470 | // signTransaction(txParams, callback) 471 | // 472 | // The function signTransaction() needs the 473 | // function KeyStore.prototype.passwordProvider(callback) 474 | // to be set in order to run properly. 475 | // The function passwordProvider is an async function 476 | // that calls the callback(err, password) with a password 477 | // supplied by the user or by other means. 478 | // The user of the hooked web3-provider is encouraged 479 | // to write their own passwordProvider. 480 | // 481 | // Uses defaultHdPathString for the addresses. 482 | 483 | KeyStore.prototype.passwordProvider = function (callback) { 484 | 485 | var password = prompt("Enter password to continue","Enter password"); 486 | callback(null, password); 487 | 488 | } 489 | 490 | 491 | KeyStore.prototype.hasAddress = function (address, callback) { 492 | 493 | var addrToCheck = strip0x(address); 494 | 495 | if (this.ksData[this.defaultHdPathString].encPrivKeys[addrToCheck] === undefined) { 496 | callback('Address not found!', false); 497 | } 498 | else { 499 | callback(null, true); 500 | } 501 | 502 | }; 503 | 504 | KeyStore.prototype.signTransaction = function (txParams, callback) { 505 | 506 | var ethjsTxParams = {}; 507 | 508 | ethjsTxParams.from = add0x(txParams.from); 509 | ethjsTxParams.to = add0x(txParams.to); 510 | ethjsTxParams.gasLimit = add0x(txParams.gas); 511 | ethjsTxParams.gasPrice = add0x(txParams.gasPrice); 512 | ethjsTxParams.nonce = add0x(txParams.nonce); 513 | ethjsTxParams.value = add0x(txParams.value); 514 | ethjsTxParams.data = add0x(txParams.data); 515 | 516 | var txObj = new Transaction(ethjsTxParams); 517 | var rawTx = txObj.serialize().toString('hex'); 518 | var signingAddress = strip0x(txParams.from); 519 | var self = this; 520 | this.passwordProvider( function (err, password) { 521 | if (err) return callback(err); 522 | KeyStore.deriveKeyFromPassword(password, function (err, pwDerivedKey) { 523 | if (err) return callback(err); 524 | var signedTx = signing.signTx(self, pwDerivedKey, rawTx, signingAddress, self.defaultHdPathString); 525 | callback(null, '0x' + signedTx); 526 | }) 527 | }) 528 | 529 | }; 530 | 531 | 532 | module.exports = KeyStore; 533 | -------------------------------------------------------------------------------- /app/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} 6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */ -------------------------------------------------------------------------------- /app/css/bootstrap-theme.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} -------------------------------------------------------------------------------- /app/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | .btn-default, 7 | .btn-primary, 8 | .btn-success, 9 | .btn-info, 10 | .btn-warning, 11 | .btn-danger { 12 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 13 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 14 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | } 16 | .btn-default:active, 17 | .btn-primary:active, 18 | .btn-success:active, 19 | .btn-info:active, 20 | .btn-warning:active, 21 | .btn-danger:active, 22 | .btn-default.active, 23 | .btn-primary.active, 24 | .btn-success.active, 25 | .btn-info.active, 26 | .btn-warning.active, 27 | .btn-danger.active { 28 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 29 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | } 31 | .btn-default.disabled, 32 | .btn-primary.disabled, 33 | .btn-success.disabled, 34 | .btn-info.disabled, 35 | .btn-warning.disabled, 36 | .btn-danger.disabled, 37 | .btn-default[disabled], 38 | .btn-primary[disabled], 39 | .btn-success[disabled], 40 | .btn-info[disabled], 41 | .btn-warning[disabled], 42 | .btn-danger[disabled], 43 | fieldset[disabled] .btn-default, 44 | fieldset[disabled] .btn-primary, 45 | fieldset[disabled] .btn-success, 46 | fieldset[disabled] .btn-info, 47 | fieldset[disabled] .btn-warning, 48 | fieldset[disabled] .btn-danger { 49 | -webkit-box-shadow: none; 50 | box-shadow: none; 51 | } 52 | .btn-default .badge, 53 | .btn-primary .badge, 54 | .btn-success .badge, 55 | .btn-info .badge, 56 | .btn-warning .badge, 57 | .btn-danger .badge { 58 | text-shadow: none; 59 | } 60 | .btn:active, 61 | .btn.active { 62 | background-image: none; 63 | } 64 | .btn-default { 65 | text-shadow: 0 1px 0 #fff; 66 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 67 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 68 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 69 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 70 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 71 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 72 | background-repeat: repeat-x; 73 | border-color: #dbdbdb; 74 | border-color: #ccc; 75 | } 76 | .btn-default:hover, 77 | .btn-default:focus { 78 | background-color: #e0e0e0; 79 | background-position: 0 -15px; 80 | } 81 | .btn-default:active, 82 | .btn-default.active { 83 | background-color: #e0e0e0; 84 | border-color: #dbdbdb; 85 | } 86 | .btn-default.disabled, 87 | .btn-default[disabled], 88 | fieldset[disabled] .btn-default, 89 | .btn-default.disabled:hover, 90 | .btn-default[disabled]:hover, 91 | fieldset[disabled] .btn-default:hover, 92 | .btn-default.disabled:focus, 93 | .btn-default[disabled]:focus, 94 | fieldset[disabled] .btn-default:focus, 95 | .btn-default.disabled.focus, 96 | .btn-default[disabled].focus, 97 | fieldset[disabled] .btn-default.focus, 98 | .btn-default.disabled:active, 99 | .btn-default[disabled]:active, 100 | fieldset[disabled] .btn-default:active, 101 | .btn-default.disabled.active, 102 | .btn-default[disabled].active, 103 | fieldset[disabled] .btn-default.active { 104 | background-color: #e0e0e0; 105 | background-image: none; 106 | } 107 | .btn-primary { 108 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 109 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 110 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 111 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 113 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 114 | background-repeat: repeat-x; 115 | border-color: #245580; 116 | } 117 | .btn-primary:hover, 118 | .btn-primary:focus { 119 | background-color: #265a88; 120 | background-position: 0 -15px; 121 | } 122 | .btn-primary:active, 123 | .btn-primary.active { 124 | background-color: #265a88; 125 | border-color: #245580; 126 | } 127 | .btn-primary.disabled, 128 | .btn-primary[disabled], 129 | fieldset[disabled] .btn-primary, 130 | .btn-primary.disabled:hover, 131 | .btn-primary[disabled]:hover, 132 | fieldset[disabled] .btn-primary:hover, 133 | .btn-primary.disabled:focus, 134 | .btn-primary[disabled]:focus, 135 | fieldset[disabled] .btn-primary:focus, 136 | .btn-primary.disabled.focus, 137 | .btn-primary[disabled].focus, 138 | fieldset[disabled] .btn-primary.focus, 139 | .btn-primary.disabled:active, 140 | .btn-primary[disabled]:active, 141 | fieldset[disabled] .btn-primary:active, 142 | .btn-primary.disabled.active, 143 | .btn-primary[disabled].active, 144 | fieldset[disabled] .btn-primary.active { 145 | background-color: #265a88; 146 | background-image: none; 147 | } 148 | .btn-success { 149 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 150 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 151 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 152 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 153 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 154 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 155 | background-repeat: repeat-x; 156 | border-color: #3e8f3e; 157 | } 158 | .btn-success:hover, 159 | .btn-success:focus { 160 | background-color: #419641; 161 | background-position: 0 -15px; 162 | } 163 | .btn-success:active, 164 | .btn-success.active { 165 | background-color: #419641; 166 | border-color: #3e8f3e; 167 | } 168 | .btn-success.disabled, 169 | .btn-success[disabled], 170 | fieldset[disabled] .btn-success, 171 | .btn-success.disabled:hover, 172 | .btn-success[disabled]:hover, 173 | fieldset[disabled] .btn-success:hover, 174 | .btn-success.disabled:focus, 175 | .btn-success[disabled]:focus, 176 | fieldset[disabled] .btn-success:focus, 177 | .btn-success.disabled.focus, 178 | .btn-success[disabled].focus, 179 | fieldset[disabled] .btn-success.focus, 180 | .btn-success.disabled:active, 181 | .btn-success[disabled]:active, 182 | fieldset[disabled] .btn-success:active, 183 | .btn-success.disabled.active, 184 | .btn-success[disabled].active, 185 | fieldset[disabled] .btn-success.active { 186 | background-color: #419641; 187 | background-image: none; 188 | } 189 | .btn-info { 190 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 191 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 192 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 193 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 195 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 196 | background-repeat: repeat-x; 197 | border-color: #28a4c9; 198 | } 199 | .btn-info:hover, 200 | .btn-info:focus { 201 | background-color: #2aabd2; 202 | background-position: 0 -15px; 203 | } 204 | .btn-info:active, 205 | .btn-info.active { 206 | background-color: #2aabd2; 207 | border-color: #28a4c9; 208 | } 209 | .btn-info.disabled, 210 | .btn-info[disabled], 211 | fieldset[disabled] .btn-info, 212 | .btn-info.disabled:hover, 213 | .btn-info[disabled]:hover, 214 | fieldset[disabled] .btn-info:hover, 215 | .btn-info.disabled:focus, 216 | .btn-info[disabled]:focus, 217 | fieldset[disabled] .btn-info:focus, 218 | .btn-info.disabled.focus, 219 | .btn-info[disabled].focus, 220 | fieldset[disabled] .btn-info.focus, 221 | .btn-info.disabled:active, 222 | .btn-info[disabled]:active, 223 | fieldset[disabled] .btn-info:active, 224 | .btn-info.disabled.active, 225 | .btn-info[disabled].active, 226 | fieldset[disabled] .btn-info.active { 227 | background-color: #2aabd2; 228 | background-image: none; 229 | } 230 | .btn-warning { 231 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 232 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 233 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 234 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 236 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 237 | background-repeat: repeat-x; 238 | border-color: #e38d13; 239 | } 240 | .btn-warning:hover, 241 | .btn-warning:focus { 242 | background-color: #eb9316; 243 | background-position: 0 -15px; 244 | } 245 | .btn-warning:active, 246 | .btn-warning.active { 247 | background-color: #eb9316; 248 | border-color: #e38d13; 249 | } 250 | .btn-warning.disabled, 251 | .btn-warning[disabled], 252 | fieldset[disabled] .btn-warning, 253 | .btn-warning.disabled:hover, 254 | .btn-warning[disabled]:hover, 255 | fieldset[disabled] .btn-warning:hover, 256 | .btn-warning.disabled:focus, 257 | .btn-warning[disabled]:focus, 258 | fieldset[disabled] .btn-warning:focus, 259 | .btn-warning.disabled.focus, 260 | .btn-warning[disabled].focus, 261 | fieldset[disabled] .btn-warning.focus, 262 | .btn-warning.disabled:active, 263 | .btn-warning[disabled]:active, 264 | fieldset[disabled] .btn-warning:active, 265 | .btn-warning.disabled.active, 266 | .btn-warning[disabled].active, 267 | fieldset[disabled] .btn-warning.active { 268 | background-color: #eb9316; 269 | background-image: none; 270 | } 271 | .btn-danger { 272 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 273 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 274 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 275 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 276 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 277 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 278 | background-repeat: repeat-x; 279 | border-color: #b92c28; 280 | } 281 | .btn-danger:hover, 282 | .btn-danger:focus { 283 | background-color: #c12e2a; 284 | background-position: 0 -15px; 285 | } 286 | .btn-danger:active, 287 | .btn-danger.active { 288 | background-color: #c12e2a; 289 | border-color: #b92c28; 290 | } 291 | .btn-danger.disabled, 292 | .btn-danger[disabled], 293 | fieldset[disabled] .btn-danger, 294 | .btn-danger.disabled:hover, 295 | .btn-danger[disabled]:hover, 296 | fieldset[disabled] .btn-danger:hover, 297 | .btn-danger.disabled:focus, 298 | .btn-danger[disabled]:focus, 299 | fieldset[disabled] .btn-danger:focus, 300 | .btn-danger.disabled.focus, 301 | .btn-danger[disabled].focus, 302 | fieldset[disabled] .btn-danger.focus, 303 | .btn-danger.disabled:active, 304 | .btn-danger[disabled]:active, 305 | fieldset[disabled] .btn-danger:active, 306 | .btn-danger.disabled.active, 307 | .btn-danger[disabled].active, 308 | fieldset[disabled] .btn-danger.active { 309 | background-color: #c12e2a; 310 | background-image: none; 311 | } 312 | .thumbnail, 313 | .img-thumbnail { 314 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 315 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 316 | } 317 | .dropdown-menu > li > a:hover, 318 | .dropdown-menu > li > a:focus { 319 | background-color: #e8e8e8; 320 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 321 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 322 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 323 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 325 | background-repeat: repeat-x; 326 | } 327 | .dropdown-menu > .active > a, 328 | .dropdown-menu > .active > a:hover, 329 | .dropdown-menu > .active > a:focus { 330 | background-color: #2e6da4; 331 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 332 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 333 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 334 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 336 | background-repeat: repeat-x; 337 | } 338 | .navbar-default { 339 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 340 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 342 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 344 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 345 | background-repeat: repeat-x; 346 | border-radius: 4px; 347 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 348 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 349 | } 350 | .navbar-default .navbar-nav > .open > a, 351 | .navbar-default .navbar-nav > .active > a { 352 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 353 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 354 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 355 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 357 | background-repeat: repeat-x; 358 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 359 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 360 | } 361 | .navbar-brand, 362 | .navbar-nav > li > a { 363 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 364 | } 365 | .navbar-inverse { 366 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 367 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 368 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 369 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 370 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 371 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 372 | background-repeat: repeat-x; 373 | border-radius: 4px; 374 | } 375 | .navbar-inverse .navbar-nav > .open > a, 376 | .navbar-inverse .navbar-nav > .active > a { 377 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 378 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 379 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 380 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 382 | background-repeat: repeat-x; 383 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 384 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 385 | } 386 | .navbar-inverse .navbar-brand, 387 | .navbar-inverse .navbar-nav > li > a { 388 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 389 | } 390 | .navbar-static-top, 391 | .navbar-fixed-top, 392 | .navbar-fixed-bottom { 393 | border-radius: 0; 394 | } 395 | @media (max-width: 767px) { 396 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 397 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 398 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 399 | color: #fff; 400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 405 | background-repeat: repeat-x; 406 | } 407 | } 408 | .alert { 409 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 410 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 411 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 412 | } 413 | .alert-success { 414 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 415 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 416 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 417 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 418 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 419 | background-repeat: repeat-x; 420 | border-color: #b2dba1; 421 | } 422 | .alert-info { 423 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 424 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 425 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 426 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 428 | background-repeat: repeat-x; 429 | border-color: #9acfea; 430 | } 431 | .alert-warning { 432 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 433 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 435 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 437 | background-repeat: repeat-x; 438 | border-color: #f5e79e; 439 | } 440 | .alert-danger { 441 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 442 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 443 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 444 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 445 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 446 | background-repeat: repeat-x; 447 | border-color: #dca7a7; 448 | } 449 | .progress { 450 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 451 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 453 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 455 | background-repeat: repeat-x; 456 | } 457 | .progress-bar { 458 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 459 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 461 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 463 | background-repeat: repeat-x; 464 | } 465 | .progress-bar-success { 466 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 467 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 469 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 471 | background-repeat: repeat-x; 472 | } 473 | .progress-bar-info { 474 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 475 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 476 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 477 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 478 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 479 | background-repeat: repeat-x; 480 | } 481 | .progress-bar-warning { 482 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 483 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 484 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 485 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 486 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 487 | background-repeat: repeat-x; 488 | } 489 | .progress-bar-danger { 490 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 491 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 492 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 493 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 494 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 495 | background-repeat: repeat-x; 496 | } 497 | .progress-bar-striped { 498 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 499 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 500 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 501 | } 502 | .list-group { 503 | border-radius: 4px; 504 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 505 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 506 | } 507 | .list-group-item.active, 508 | .list-group-item.active:hover, 509 | .list-group-item.active:focus { 510 | text-shadow: 0 -1px 0 #286090; 511 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 512 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 513 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 514 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 515 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 516 | background-repeat: repeat-x; 517 | border-color: #2b669a; 518 | } 519 | .list-group-item.active .badge, 520 | .list-group-item.active:hover .badge, 521 | .list-group-item.active:focus .badge { 522 | text-shadow: none; 523 | } 524 | .panel { 525 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 526 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 527 | } 528 | .panel-default > .panel-heading { 529 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 530 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 531 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 532 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 533 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 534 | background-repeat: repeat-x; 535 | } 536 | .panel-primary > .panel-heading { 537 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 538 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 539 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 540 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 541 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 542 | background-repeat: repeat-x; 543 | } 544 | .panel-success > .panel-heading { 545 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 546 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 547 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 548 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 549 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 550 | background-repeat: repeat-x; 551 | } 552 | .panel-info > .panel-heading { 553 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 554 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 555 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 556 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 557 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 558 | background-repeat: repeat-x; 559 | } 560 | .panel-warning > .panel-heading { 561 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 562 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 563 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 564 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 565 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 566 | background-repeat: repeat-x; 567 | } 568 | .panel-danger > .panel-heading { 569 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 570 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 571 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 572 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 573 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 574 | background-repeat: repeat-x; 575 | } 576 | .well { 577 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 578 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 579 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 580 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 581 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 582 | background-repeat: repeat-x; 583 | border-color: #dcdcdc; 584 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 585 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 586 | } 587 | /*# sourceMappingURL=bootstrap-theme.css.map */ 588 | --------------------------------------------------------------------------------