Compare commits

...

211 Commits

Author SHA1 Message Date
bumi a75bcc6a59 Add configurable network name
This is required for the infura provider
2018-04-22 20:25:58 +02:00
bumi f5dc8f86cf Use infura hosted provider if web3 is not available
If the user does not have metamask or similar (no web3 provider is
availale) we will load data from the infura node.

No signer is available for the infura node but the ethers provider would
throw an error if we try to access it - so this moves the decision of
choosing the provider to the ethProvider function.
2018-04-22 18:41:51 +02:00
basti 3e7fe1b5be Add npm command for running on kovan 2018-04-21 20:19:21 +02:00
basti 552ca3026d Update truffle-kredits 2018-04-21 20:19:01 +02:00
fsmanuel ba50e5a878 Merge pull request #58 from 67P/cleanup/kredits-service
Cleanup kredits service
2018-04-21 10:09:35 +00:00
fsmanuel acd1ce28f4 Use .init() instead of static .setup() 2018-04-21 11:58:17 +02:00
fsmanuel 1b2b1bd447 Fix contributor deserialize test 2018-04-21 11:34:41 +02:00
fsmanuel 7083d2369d Fix eslint 2018-04-21 11:20:47 +02:00
fsmanuel 8fed06b494 Rename initEthProvider to getEthProvider 2018-04-21 11:15:13 +02:00
fsmanuel 75650c19ab Cleanup kredits service 2018-04-21 11:05:02 +02:00
fsmanuel ade47dadde Lock ethers.js at 3.0.15 2018-04-21 10:43:03 +02:00
fsmanuel eaec617756 Merge pull request #57 from 67P/use-kredits-npm-lib
Remove kredits library files and use npm package
2018-04-18 22:50:41 +00:00
fsmanuel 2199394abc Use kredits-contracts master 2018-04-19 00:44:34 +02:00
fsmanuel 93a18a8a20 Remove unused dependencies 2018-04-19 00:10:43 +02:00
fsmanuel 2190b7af68 Remove in-repo addon 2018-04-18 22:55:49 +02:00
bumi 7da4956f1e Fix tests
These tests must be moved into the kredits-contract package. The
serialization is happening there.
They should be ported to mocha/chai probably
2018-04-18 20:19:26 +02:00
bumi 31e27865bb Use kredits-contracts from npm 2018-04-18 19:26:35 +02:00
bumi 967f213cba Remove build script to write address/abi json
those are now in the kredits-contracts library
2018-04-18 19:24:38 +02:00
bumi 0e556aa794 Remove kredits library files and use npm package
Currently it uses the package directly from git
2018-04-18 19:01:26 +02:00
basti e2ac6099e3 Merge pull request #56 from 67P/naming-conventions
Adjust for latest naming conventions
2018-04-17 13:29:08 +02:00
basti a4088e7acf Merge branch 'master' into naming-conventions 2018-04-17 12:55:20 +02:00
basti 550bd48fdb Merge pull request #55 from 67P/dev/ember_upgrade
Update Ember to 3.1
2018-04-17 12:53:30 +02:00
bumi f0f82a96fb Adjust for latest naming conventions
expept the renaming of ipfsHash to hashDigest all are already reflected
in the contracts
2018-04-17 11:43:44 +02:00
basti 8d303ea5af Fix tests, remove obsolete initializers 2018-04-17 01:38:21 +02:00
basti c0b5c36115 Don't inject kredits service in models 2018-04-16 19:23:28 +02:00
basti ba3c707da3 Fix ESLint errors 2018-04-16 19:14:37 +02:00
basti b09f19f399 Update Ember to 3.1 2018-04-16 18:11:52 +02:00
bumi 539f32911d Merge pull request #53 from 67P/docs/readme_local_contracts
Update README
2018-04-16 15:12:20 +00:00
basti e009caaf25 Add ember-cli-update 2018-04-16 13:38:27 +02:00
basti 1761ffbd3b Update README
Improve description and instructions for running against a local
development chain/node and IPFS node.
2018-04-16 12:49:49 +02:00
bumi 33d6499dd4 Merge pull request #51 from 67P/feature/contract-events
Move contract event handling into service
2018-04-15 19:58:23 +00:00
fsmanuel 3839eb2be4 Fixes and cleanup 2018-04-15 21:33:37 +02:00
fsmanuel a04ab09ed6 Move event handler assignment into method 2018-04-15 20:49:17 +02:00
fsmanuel 89d6b920b1 Remove unused Ember import 2018-04-15 20:38:24 +02:00
fsmanuel d425ed6b9e Move contract event handling into service 2018-04-15 20:36:21 +02:00
fsmanuel e4215557c8 Merge pull request #49 from 67P/feature/store-models-in-service
Move contributors + proposals to service
2018-04-15 18:35:10 +00:00
fsmanuel fcc4045a4e Merge branch 'master' into feature/store-models-in-service
# Conflicts:
#	app/controllers/index.js
#	tests/unit/controllers/index-test.js
2018-04-15 20:23:39 +02:00
fsmanuel 5bcce71b36 Merge pull request #50 from 67P/refactor/naming-conventions
Refactor/naming conventions
2018-04-15 18:17:47 +00:00
fsmanuel c9abec8a46 Merge pull request #47 from 67P/features/initialize-error-handling
Fail smarter with better error handling
2018-04-15 18:14:12 +00:00
bumi 62e195d186 remove fake bignumber from tests
We make everything bignumber-y now so that is no longer needed
2018-04-15 19:56:03 +02:00
fsmanuel e5f69a5e9a Remove unused computed 2018-04-15 19:09:16 +02:00
bumi e31ff492d5 merge master 2018-04-15 19:08:26 +02:00
fsmanuel 99bbcda709 Move contributors + proposals to service
With this change we have all the contributors + proposals all the time in one place and can update them via events.
2018-04-15 19:02:57 +02:00
bumi 8aa941e704 Rename recipientId to contributorId in proposals 2018-04-15 19:02:35 +02:00
bumi fdda30da67 Merge pull request #48 from 67P/revert-45-refactor/naming-conventions
Revert "Adjust for new naming conventions"
2018-04-15 16:42:36 +00:00
bumi 399ee27af9 Revert "Adjust for new naming conventions" 2018-04-15 16:35:31 +00:00
bumi d2c43b9ef1 Merge pull request #45 from 67P/refactor/naming-conventions
Adjust for new naming conventions
2018-04-15 16:24:17 +00:00
bumi 7e6c6a037d code style. use interpolation syntax 2018-04-15 18:02:52 +02:00
bumi e1f19a1832 Fail smarter with better error handling
This should give more insights in case of an error during loading data
from Ethereum and IPFS.
2018-04-15 16:45:09 +02:00
bumi 7b5cea3613 Adjust for new naming conventions 2018-04-10 18:46:54 +02:00
fsmanuel 5d875d23e2 Merge pull request #44 from 67P/fix/proposal-loading
Fix proposal loading
2018-04-10 17:48:53 +02:00
bumi e24fc6c81d Fix proposal loading
we changed the storage of proposals from an array to a mapping.
Because of this keys/ids now no longer start with zero but with 1.
2018-04-10 16:29:18 +02:00
bumi 72c6266474 Merge pull request #42 from 67P/kredits-module
Refactor contract interaction in its own module
2018-04-10 13:46:57 +00:00
bumi 4a711788f6 cleanup 2018-04-10 15:27:38 +02:00
bumi c373c901a2 Fix test to work with the bignumber depencency 2018-04-09 20:54:07 +02:00
bumi 32846194b6 Handle bignumber conversions
The kredits module always returns raw data from the contract. For uint
values these are bignumbers.
To handle those in the ember app we need to convert them to string or to
number.
2018-04-09 20:31:34 +02:00
bumi 1a5d33b1f0 Move IPFS handling in utils class 2018-04-09 19:28:49 +02:00
bumi 0e330a6529 Cleanup 2018-04-09 15:15:33 +02:00
bumi e9f0638ca2 Unify Contributor/Proposal saving 2018-04-09 15:05:13 +02:00
bumi ce8fef79dc Search proposal contributor by stringified ID
Kredits returns default web3/ethers.js values which are BigNumbers.
Internally we use Strings. We need to do this somewhere centrally
though.
2018-04-09 14:58:50 +02:00
bumi b9fa0e2d60 typo 2018-04-09 14:58:25 +02:00
bumi 41a04d6b53 Refactor IPFS hash handling
This now uses the multihashes dependency (which is an ipfs dependency)
to decode/encode the ipfs hashes.
2018-04-09 14:52:39 +02:00
fsmanuel e527099b00 Fix tests 2018-04-09 02:26:03 +02:00
fsmanuel 7e04cef964 Extract kredits module 2018-04-09 02:09:38 +02:00
bumi cb1208acaa Merge pull request #41 from 67P/refactor/cleanup-models
Cleanup controller, templates and service
2018-04-08 12:56:05 +00:00
fsmanuel f985ce6402 So sad that I can not force push! 2018-04-08 14:41:08 +02:00
fsmanuel f54a36f240 Use getProposalById instead of buildModel 2018-04-08 14:33:38 +02:00
bumi 4182f0993c Merge pull request #39 from 67P/refactor/contributor-management
Use contributors contract to add new contributors
2018-04-08 12:12:52 +00:00
fsmanuel 28f7466595 Cleanup controller, templates and service 2018-04-08 14:06:16 +02:00
bumi 6cfd4240de Merge pull request #38 from 67P/refactor/proposal
Refactor proposals
2018-04-08 12:02:28 +00:00
bumi 6e24015f76 Readme 2018-04-08 13:54:50 +02:00
bumi 5390c5cea8 Use contributors contract to add new contributors
related: https://github.com/67P/truffle-kredits/pull/17
2018-04-08 13:17:35 +02:00
fsmanuel adcc81ba52 Move tests 2018-04-08 02:27:56 +02:00
fsmanuel 31a1f21dcd Fix linting 2018-04-08 02:13:07 +02:00
fsmanuel b7316e15a2 Align *Proposal to *Contributor methods 2018-04-08 02:01:10 +02:00
fsmanuel ad92ffa447 Extract serializer 2018-04-08 02:01:10 +02:00
fsmanuel 45044c0f61 Refactor controller and route
I’m not sure how the queryParams are used so I removed them. I’m happy to readd them.
2018-04-08 02:01:10 +02:00
fsmanuel cc7c47cbab Refactor proposal form 2018-04-08 02:01:02 +02:00
fsmanuel 27f4006f05 Merge pull request #37 from 67P/refactor/get-contributor-by-id
Refactor getContributorById to be move generic
2018-04-08 01:32:46 +02:00
fsmanuel 576313a7b0 Update according to suggestions 2018-04-08 01:25:59 +02:00
bumi 13c3feaa69 Merge pull request #36 from 67P/fix/contributor-ipfshash
Replace missed profileHash with ipfsHash
2018-04-07 23:09:03 +00:00
fsmanuel a0dd8b925b Refactor getContributorById to be move generic
We can use this generic functions also for the upcomig proposal refactoring.
2018-04-08 00:54:42 +02:00
fsmanuel 21f187cac2 Replace missed profileHash with ipfsHash 2018-04-08 00:44:25 +02:00
fsmanuel d8951caddb Block in application route if accountNeedsUnlock
The index route never gets invoked if you reload on the proposal page. That change makes sure we always initialize `ethProvider`.
2018-04-07 22:43:22 +00:00
fsmanuel 206c657b98 Merge pull request #34 from 67P/features/balance-from-contributors
Use getContributorById call to get contributor data
2018-04-07 22:22:35 +02:00
fsmanuel 6e262537a4 Extract multihash functions 2018-04-07 18:47:16 +00:00
bumi 0587559939 Use getContributorById call to get contributor data
This new call also returns the balance of the contributor so we don't
have to make an extra call to the token contract.
2018-04-07 20:33:41 +02:00
fsmanuel 939baec1a8 Add alert message after saved 2018-04-07 17:22:33 +00:00
fsmanuel 429f670c12 Better assign 2018-04-07 17:22:33 +00:00
fsmanuel b21d2ad864 Extract serializer logic into lib
To use the same code in hubot I extracted the serializer logic into lib to later move it into `truffle-kredits` when we are done with all other common logic.
2018-04-07 17:22:33 +00:00
fsmanuel 8f841522a3 Refactor add-contributor component to DDAU 2018-04-07 17:22:33 +00:00
fsmanuel fa7db66f4e Merge pull request #31 from 67P/feature/account-badge
Add account badge
2018-04-06 17:54:53 +02:00
bumi 499b22af15 Fix JS/Ember style 2018-04-06 17:40:45 +02:00
bumi f992fb10bd Fix JS style 2018-04-06 17:00:25 +02:00
bumi 603224e906 Use ember style empty promise 2018-04-06 16:53:01 +02:00
bumi bbea07b2a7 Add account badge
This gives easy access to test if the "user/visitor" has an account, is
a contributor and if she is core.
This is used to display some account information.
2018-04-06 16:45:55 +02:00
bumi b9d78efaff More details about running the app locally 2018-04-04 15:43:52 +02:00
fsmanuel 1a58099413 Merge pull request #29 from 67P/ethers
Use ethers.js instead of the old web3
2018-04-04 14:20:55 +02:00
bumi e2d02d357c linting 2018-04-04 14:15:07 +02:00
bumi 0812ff3467 Add comment about ethProvider loading.
It is loaded in the route and thus available for the rest of the
application.
2018-04-04 14:02:27 +02:00
bumi 6dafc26deb Refactor web3 initialization
This changes how we wait for web3 and accounts to be available and
renames web3Provider to ethProvider as we could be able to swap the
different providers (ethers.js vs. web3)
2018-04-04 13:56:41 +02:00
bumi a418ddbe52 fix tests 2018-04-04 12:49:41 +02:00
bumi 6a5eb5a313 use kovan as default network id 2018-04-03 21:34:45 +02:00
bumi 6354b03717 cleanup 2018-04-03 20:32:46 +02:00
bumi 5d6b4a5255 fix linting errors 2018-04-03 19:19:11 +02:00
bumi 3f0b1ccbcd Use ethers.js instead of the old web3
the web3 we are using is old and everything will change with web3 1.0
which is currently in beta for ages.
ethers.js seems to be a bit more lightweight and implements pretty much
the same API so let's give it a try.
2018-04-03 18:35:29 +02:00
fsmanuel f00ecfd9ab Merge pull request #28 from 67P/refactor/proposal-ipfs-hash
Handle new IPFS hashes in proposal
2018-04-02 21:00:58 +02:00
fsmanuel ee0d3370b1 Fix travis 2018-04-02 20:40:59 +02:00
fsmanuel 73d7ed52dd Refactor contributor 2018-04-02 19:08:29 +02:00
fsmanuel 6a31b0d579 Rename contributor.kredits to .balance 2018-04-02 19:07:42 +02:00
bumi 869c4c0ae9 Handle new IPFS hashes in proposal 2018-04-02 18:19:22 +02:00
bumi 4be696ea62 Fix contract events handling 2018-04-02 18:18:34 +02:00
bumi 83a0d56314 Update ipfs-api dependency 2018-04-02 18:17:50 +02:00
fsmanuel 0d1418e595 Fix package.json 2018-04-01 15:24:45 +02:00
fsmanuel 7db521a72a Fix kredits-contracts dependency 2018-04-01 15:23:05 +02:00
fsmanuel e7ff606891 Update readme 2018-04-01 15:21:14 +02:00
bumi 60de3d8a31 Merge pull request #27 from 67P/truffle-contracts
Truffle contracts
2018-04-01 12:19:21 +00:00
fsmanuel f12740dfa8 Update kredits service 2018-04-01 03:20:16 +02:00
fsmanuel 020a1c5682 In repo contracts addon 2018-04-01 03:16:22 +02:00
basti 576e2741a9 More contract-related adjustments 2017-08-14 15:31:58 +02:00
basti bc19363bf9 Update kosmos-schemas 2017-08-12 14:05:31 +02:00
basti 72cf4b495e Adjust for new contract wrapper (WIP) 2017-08-12 13:59:18 +02:00
basti b1db64a07d Merge pull request #23 from 67P/feature/22-handle_locked_metamask
Ask user to unlock their account
2017-06-14 22:15:18 +02:00
basti 22affa7663 Edit account-unlock message/hint 2017-06-14 07:43:38 +02:00
galfert b125d941ba Ask user to unlock their account 2017-06-14 01:55:39 +02:00
basti 9b37f130f3 Merge pull request #21 from 67P/feature/wait_for_web3
Pause app boot until window load event fired
2017-06-13 14:37:19 +02:00
basti 993cd85e4a Improve waiting for window.onload
In case the window load event was fired before getting to the Ember
initializer, it would pause app loading indefinitely. This makes sure
that app loading is only paused, if there's still an event to wait for.
2017-06-13 14:27:09 +02:00
galfert e51586a78c Pause app boot until window load event fired
Refs #17

Allows the user provided Web3 instance to be inserted before we
try to use it.
2017-06-13 13:35:44 +02:00
basti 8aa86fa553 Merge pull request #16 from 67P/feature/ipfs-metadata
Move all metadata to IPFS with proper schemas
2017-06-09 00:28:14 +02:00
basti 510f7de693 Use HTTPS for production IPFS 2017-06-08 23:56:43 +02:00
basti 918ae5b2d1 Fix property dep 2017-06-08 17:06:19 +02:00
basti 6263254a1b Specify radix parameter for parsing amount 2017-06-08 16:53:40 +02:00
basti 99e921c302 Throw error when file couldn't be fetched from IPFS 2017-06-08 16:42:16 +02:00
basti 7f2f781a77 Don't try to save proposal when invalid 2017-06-08 16:41:53 +02:00
basti 3571cc633e Improve computed-property dependency 2017-06-08 16:41:03 +02:00
basti 2011636173 Add contribution details as title 2017-06-07 13:01:35 +02:00
basti cbb15f4d6f Fix mobile layout, add contribution kind to props 2017-06-07 12:46:03 +02:00
basti 33072900e9 Use contributor's display name for proposal list 2017-06-07 12:45:30 +02:00
basti c98a50cd42 Fix mobile layout
Unfortunately also breaks the animation when opening contributor
details for now. Couldn't get it to work again.
2017-06-07 11:11:01 +02:00
basti 1f41dbcae5 Load contribution details from IPFS 2017-06-07 10:35:57 +02:00
basti a918719b8f Decrease overall UI size a bit 2017-06-07 10:35:36 +02:00
basti 7b91e64af4 Create contribution IPFS objects, attach to proposals 2017-06-06 21:27:05 +02:00
basti 0afbeea01b Use newly generated UUID as ID for new contributor 2017-06-06 19:57:24 +02:00
basti 0510cf2389 Set contributor kind from IPFS profile 2017-06-06 19:56:55 +02:00
basti c893f91ce2 Improve logging 2017-06-06 19:56:40 +02:00
basti eced2f423f Minimal validation for amount 2017-06-06 19:56:15 +02:00
basti 6eeb505701 Use correct property for dep 2017-06-06 19:55:59 +02:00
basti 4cb3f5d8ba Use profile data from IPFS for contributor models 2017-06-06 19:05:42 +02:00
basti 372a8a9b2d Unify author value across packages 2017-06-06 13:33:41 +02:00
basti a714601c63 Update kosmos-schemas
This should fix the CI builds.
2017-06-05 22:10:10 +02:00
basti 240d446b97 Remove obsolete output 2017-06-05 22:08:13 +02:00
basti 33e81a7668 Store proper contributor profile in IPFS 2017-06-05 21:58:46 +02:00
bumi f8b75cca61 Manually set networkId
By default kredits-contracts gets the networkId from the web3 object.
This causes some random JSON RPC errors when using MetaMask:
https://github.com/MetaMask/metamask-plugin/issues/1538

This set the default networkId to 17 which is our custom private
network. For development purposes the networkId can also be set
dynamically through the config:networkId local storage entry.
2017-06-04 19:28:14 +02:00
bumi 32e1bbd617 no more evil while loop 2017-06-04 19:27:59 +02:00
basti 1de26004ab Use correct properties as dependency 2017-06-04 17:09:21 +02:00
bumi 0a66c19b0a Specify localhost as web3 provider URL for local deployments 2017-06-01 15:06:52 +02:00
basti 40d6b9905a Observe kredits property in computed array 2017-05-31 17:06:50 +02:00
basti 228825053c Update metadata, version 2017-05-31 16:54:08 +02:00
basti eda0f7a467 Hide new-proposal link for non-wallet users 2017-05-20 21:25:48 +02:00
basti 3052c9dfc5 Merge pull request #15 from 67P/feature/12-new_proposal_contributor_list
Choose contributors from list for new proposals
2017-05-20 18:52:41 +02:00
galfert dbb4094eab Merge pull request #14 from 67P/feature/5-public_ipfs
Use public IPFS node, improve config
2017-05-20 18:14:44 +02:00
galfert f875cd7b91 Choose contributors from list for new proposals 2017-05-20 02:37:11 +02:00
basti 45f17100a2 Use public IPFS node, improve config
Only use local IPFS in development. Set default parity provider URL and
overwrite if set in ENV.
2017-05-16 15:28:11 +02:00
basti dda5a8d350 Merge pull request #11 from 67P/feature/contributors_list
Contributor list improvements
2017-05-14 19:28:17 +02:00
basti 4f57ceaee9 Remove debug log statement 2017-05-14 16:27:43 +02:00
basti 2405e3e118 Show correct number for kredits distribution 2017-05-14 16:25:02 +02:00
basti d7aaadea2b Only show conributors with Kredits in list 2017-05-14 16:16:12 +02:00
basti 8d07c7d741 Update page title 2017-05-14 15:18:15 +02:00
basti 2b260bccd5 Merge pull request #10 from 67P/feature/locally-configurable-provider
Add support to locally conifgure web3 provider
2017-05-14 15:05:31 +02:00
bumi 7299394809 Add support to locally conifgure web3 provider
If no web3 is provided from the browser we check if a
config:web3ProviderUrl item is set in localstorage.
If yes we connect to that provider otherwise use the default config.
Helpful for working simultanously with different chains and providers.
2017-05-14 12:37:47 +02:00
basti 0b7afc0dd5 Update kredits, be expressive about contract options 2017-05-14 01:09:53 +02:00
basti f57e90941c Merge pull request #9 from 67P/configurable-provider
Make web3 provider URL configurable; default to infura
2017-05-13 17:06:33 +02:00
basti be6c5574ff Change default Ethereum RPC provider
Use our own Parity node.
2017-05-13 17:00:04 +02:00
bumi 859cca3820 Merge 2017-05-12 20:04:28 +02:00
bumi ba1cd1cd49 Make web3 provider URL configurable; default to infura
This uses infura (kovan network) as the default web3 provider URL
2017-05-12 17:17:20 +02:00
bumi a1f73b0307 Readme about locally deployed contracts 2017-05-08 13:02:26 +02:00
bumi 3be14b945a Use kredits-contracts from npm
This also configures the npm scripts to work with locally deployed
contracts
2017-05-08 13:00:33 +02:00
bumi 675dff7314 Add support for new splitted Token contract 2017-05-05 20:25:57 +02:00
basti 2ff76c902b Update author info 2017-05-03 17:22:06 +02:00
galfert 642015700e Don't resolve promises after rejecting them 2017-05-03 11:53:21 +02:00
galfert 969ef8d1ed Add proposal form
Closes #6

Form can be prefilled using query params on the route. The params are
`recipient`, `amount`, `url` and `ipfsHash`.
2017-05-03 11:52:42 +02:00
basti b5f8216f7f Show checkmark placeholder 2017-02-19 13:53:19 +08:00
basti e6740d9b4f Funky custom checkmark 2017-02-19 13:49:54 +08:00
basti 2e61b4c8ad Add checkbox for isCore 2017-02-19 11:44:24 +08:00
basti 62a0733a11 Use corrrect (temporary) placeholder 2017-02-19 11:16:51 +08:00
basti 09aa8a5669 Remove obsolete TODO 2017-02-19 11:15:18 +08:00
basti 01c1a9c804 Toggle metadata visibility by clicking name 2017-02-19 11:13:14 +08:00
basti 0a2f44cec8 Add metadata row to contributor list
Contains links to Ethereum address and IPFS content.
2017-02-19 10:30:56 +08:00
basti e7fa188797 Store contributor profile data in IPFS
This stores the GitHub username and UID in IPFS, and stores the
resulting IPFS hash in the contract. Now we can remove the GitHub
profile data from the contract entirely.
2017-02-18 18:44:14 +08:00
basti b8ded8e6a8 0.1.0 2017-02-18 18:05:39 +08:00
basti 26cebc11c6 Add IPFS service (#4)
Add IPFS service

Add the `ipfs-api` JS library and a service using the browserified
version of it. The service has functions for storing and reading file
contests.
2017-02-18 09:48:26 +00:00
bumi 2a9f9a50c4 use public infura ETH node in production -still on ropsten 2017-02-08 19:22:16 +08:00
basti 7c98de3e6a Comment unused test 2017-02-08 14:34:14 +08:00
basti cd6fbfe17c Mark current user in contrib list if available 2017-02-08 14:22:49 +08:00
basti ad76fb2369 Warn non-Ethereum users when trying to add contributor 2017-02-08 14:01:19 +08:00
bumi 0f9664410b code style, missing semicolon 2017-02-08 13:58:19 +08:00
bumi d05356cfe4 connect add contributor form to ethereum 2017-02-08 13:46:40 +08:00
basti 1de2e002bb Show proposals to everyone, with warning for non-members 2017-02-08 13:22:19 +08:00
bumi 7a072f2586 use dev chain in development environment 2017-02-08 13:15:06 +08:00
basti 3eb58451cc Use testnet for all envs for now 2017-02-07 22:38:15 +08:00
basti 9cc4f85a69 Use parity.kosmos.org 2017-02-07 21:35:55 +08:00
basti 29602d5c36 Change page title 2017-02-07 21:35:44 +08:00
basti b15191aa0e Add deployment 2017-02-07 21:12:08 +08:00
bumi d6d4e51ea3 connect to contracts depending on the environment 2017-02-07 13:28:37 +08:00
basti bb3696addc Handle transfer event 2017-02-06 23:22:43 +08:00
basti 96ea4bb993 Don't handle event for already-executed proposals 2017-02-06 22:23:36 +08:00
basti 433d6b0d59 Handle ProposalVoted 2017-02-05 23:11:39 +08:00
bumi 0235313282 load kredits contract definition from kredits-contracts package 2017-02-05 22:15:11 +08:00
basti 10763b1e7f Update balance on ProposalExecuted 2017-02-05 21:52:12 +08:00
basti 1f39884eef Handle ProposalExecuted event 2017-02-05 21:31:00 +08:00
basti d0e10babfe Watch contract events (and add new proposals) 2017-02-05 19:27:17 +08:00
174 changed files with 28091 additions and 694 deletions
-4
View File
@@ -1,4 +0,0 @@
{
"directory": "bower_components",
"analytics": false
}
+1 -1
View File
@@ -5,5 +5,5 @@
Setting `disableAnalytics` to true will prevent any data from being sent. Setting `disableAnalytics` to true will prevent any data from being sent.
*/ */
"disableAnalytics": false "disableAnalytics": true
} }
+43
View File
@@ -0,0 +1,43 @@
module.exports = {
root: true,
parserOptions: {
ecmaVersion: 2017,
sourceType: 'module'
},
globals: {
console: true
},
plugins: [
'ember'
],
extends: [
'eslint:recommended',
'plugin:ember/recommended'
],
env: {
browser: true
},
rules: {
'ember/avoid-leaking-state-in-ember-objects': 'warn',
'no-console': 'off'
},
overrides: [
// node files
{
files: [
'ember-cli-build.js',
'testem.js',
'config/**/*.js',
'lib/*/index.js'
],
parserOptions: {
sourceType: 'script',
ecmaVersion: 2015
},
env: {
browser: false,
node: true
}
}
]
};
+7
View File
@@ -14,4 +14,11 @@
/coverage/* /coverage/*
/libpeerconnection.log /libpeerconnection.log
npm-debug.log* npm-debug.log*
yarn-error.log
testem.log testem.log
.tm_properties
# ember-try
.node_modules.ember-try/
bower.json.ember-try
package.json.ember-try
-32
View File
@@ -1,32 +0,0 @@
{
"predef": [
"document",
"window",
"-Promise"
],
"browser": true,
"boss": true,
"curly": true,
"debug": false,
"devel": true,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"eqnull": true,
"esversion": 6,
"unused": true
}
+10 -9
View File
@@ -4,22 +4,23 @@ node_js:
- "6" - "6"
sudo: false sudo: false
dist: trusty
addons:
chrome: stable
cache: cache:
directories: directories:
- $HOME/.npm - $HOME/.npm
- $HOME/.cache # includes bowers cache
env:
global:
# See https://git.io/vdao3 for details.
- JOBS=1
before_install: before_install:
- npm config set spin false - npm config set spin false
- npm install -g bower
- bower --version
- npm install phantomjs-prebuilt
- node_modules/phantomjs-prebuilt/bin/phantomjs --version
install:
- npm install
- bower install
script: script:
- npm run lint:js
- npm test - npm test
+63 -7
View File
@@ -10,22 +10,23 @@ A short introduction of this app could easily go here.
You will need the following things properly installed on your computer. You will need the following things properly installed on your computer.
* [Git](https://git-scm.com/) * [Git](https://git-scm.com/)
* [Node.js](https://nodejs.org/) (with NPM) * [Node.js](https://nodejs.org/) (with npm)
* [Bower](https://bower.io/)
* [Ember CLI](https://ember-cli.com/) * [Ember CLI](https://ember-cli.com/)
* [PhantomJS](http://phantomjs.org/) * [Google Chrome](https://google.com/chrome/)
## Installation ## Installation
* `git clone <repository-url>` this repository * `git clone git@github.com:67P/kredits-web.git` this repository
* `cd kredits-web` * `cd kredits-web`
* `npm install` * `npm install`
* `bower install`
## Running / Development ## Running / Development
* `ember serve` * `ember serve`
* Visit your app at [http://localhost:4200](http://localhost:4200). * Visit your app at [http://localhost:4200](http://localhost:4200).
* Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests).
See [working with locally deployed contracts](https://github.com/67P/kredits-web#working-with-locally-deployed-contracts) for details on how to develop with locally deployed contracts.
### Code Generators ### Code Generators
@@ -36,6 +37,11 @@ Make use of the many generators for code, try `ember help generate` for more det
* `ember test` * `ember test`
* `ember test --server` * `ember test --server`
### Linting
* `npm run lint:js`
* `npm run lint:js -- --fix`
### Building ### Building
* `ember build` (development) * `ember build` (development)
@@ -43,11 +49,61 @@ Make use of the many generators for code, try `ember help generate` for more det
### Deploying ### Deploying
Specify what it takes to deploy your app. _(You need collaborator permissions on the 5apps Deploy project.)_
`npm run deploy`
## Working with locally deployed contracts
The smart contracts and their JavaScript wrapper library are developed in the
[truffle-kredits](https://github.com/67P/truffle-kredits) repo/package.
You can run `kredits-web` on your machine, against a local, simulated Ethereum
network, provided e.g. by [ganache](http://truffleframework.com/ganache/) or
[ganache-cli](https://github.com/trufflesuite/ganache-cli).
[truffle-kredits](https://github.com/67P/truffle-kredits) holds all the tools
to start and set up such a simulated network, as well as to deploy smart
contracts to it.
These are the basic steps to get up and running:
#### 1. IPFS
Run a local IPFS deamon in offline mode.
* Make sure CORS headers are configured. See [IPFS](#ipfs) for more info.
* `ipfs daemon --offline`
#### 2. truffle-kredits
Get your local Ethereum development node running.
* Clone [truffle-kredits](https://github.com/67P/truffle-kredits)
* `npm install`
* `npm run ganache` - which is basically: `ganache-cli -p 7545 -i 100` (we use the non-default port for local networks and a fixed network id)
* `npm run bootstrap` - bootstrap runs fresh migrations, adds some seed data and writes the address/abi information to JSON that will be used by kredits-web
* `npm link` - make the `truffle-kredits` module linkable as `kredits-contracts` on your machine
#### 3. kredits-web
With IPFS and Ethereum/ganache running, you can now start this Ember app.
* `npm link kredits-contracts` - link the local `truffle-kredits` package (it will become `kredits-contracts` soon)
* `npm start`
#### IPFS
Install IPFS with your favorite package manager and run
ipfs init (on initial installation)
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["localhost:4200"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "GET", "POST"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials '["true"]'
## Further Reading / Useful Links ## Further Reading / Useful Links
* [ember.js](http://emberjs.com/) * [ember.js](https://emberjs.com/)
* [ember-cli](https://ember-cli.com/) * [ember-cli](https://ember-cli.com/)
* Development Browser Extensions * Development Browser Extensions
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
+2 -6
View File
@@ -1,13 +1,9 @@
import Ember from 'ember'; import Application from '@ember/application';
import Resolver from './resolver'; import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers'; import loadInitializers from 'ember-load-initializers';
import config from './config/environment'; import config from './config/environment';
let App; const App = Application.extend({
Ember.MODEL_FACTORY_INJECTIONS = true;
App = Ember.Application.extend({
modulePrefix: config.modulePrefix, modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix, podModulePrefix: config.podModulePrefix,
Resolver Resolver
+52 -23
View File
@@ -1,35 +1,64 @@
import Ember from 'ember'; import Component from '@ember/component';
import { and, notEmpty } from '@ember/object/computed';
import { inject as injectService } from '@ember/service';
export default Ember.Component.extend({
id: null, export default Component.extend({
realName: null, kredits: injectService(),
address: null,
classId: function() { // Default attributes used by reset
let value = this.get('id'); attributes: {
return (Ember.isEmpty(value)) ? null : 'valid'; account: null,
}.property('id'), name: null,
kind: 'person',
url: null,
github_username: null,
github_uid: null,
wiki_username: null,
isCore: false,
},
classRealName: function() { didInsertElement() {
let value = this.get('realName'); this._super(...arguments);
return (Ember.isEmpty(value)) ? null : 'valid'; this.reset();
}.property('realName'), },
classAddress: function() { // TODO: add proper address validation
let value = this.get('address'); isValidAccount: notEmpty('account'),
return (Ember.isEmpty(value)) ? null : 'valid'; isValidName: notEmpty('name'),
}.property('address'), isValidURL: notEmpty('url'),
isValidGithubUID: notEmpty('github_uid'),
isValidGithubUsername: notEmpty('github_username'),
isValidWikiUsername: notEmpty('wiki_username'),
isValid: and(
'isValidAccount',
'isValidName',
'isValidGithubUID'
),
reset: function() {
this.setProperties(this.attributes);
},
actions: { actions: {
submit() {
if (!this.isValid) {
alert('Invalid data. Please review and try again.');
return;
}
save() { let attributes = Object.keys(this.attributes);
console.log('id', this.get('id')); let contributor = this.getProperties(attributes);
console.log('realName', this.get('realName')); let saved = this.save(contributor);
console.log('address', this.get('address'));
// The promise handles inProgress
this.set('inProgress', saved);
saved.then(() => {
this.reset();
window.scroll(0,0);
window.alert('Contributor added.');
});
} }
} }
}); });
+55 -17
View File
@@ -1,26 +1,64 @@
<form {{action "save" on="submit"}}> <form {{action "submit" on="submit"}}>
<p> <p>
{{input name="id" {{input name="is-core"
type="text" type="checkbox"
placeholder="GitHub UID (123)" id="is-core"
value=id checked=isCore}}
class=classId}} <label for="is-core" class="checkbox">
Core team member (can add contributors)
</label>
</p> </p>
<p> <p>
{{input name="realname" {{input name="account"
type="text"
placeholder="Carl Sagan"
value=realName
class=classRealName}}
</p>
<p>
{{input name="address"
type="text" type="text"
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4" placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
value=address value=account
class=classAddress}} class=(if isValidAccount 'valid' '')}}
</p>
<p>
<select required onchange={{action (mut kind) value="target.value"}}>
<option value="person" selected={{eq kind "person"}}>Person</option>
<option value="organization" selected={{eq kind "organization"}}>Organization</option>
</select>
</p>
<p>
{{input name="name"
type="text"
placeholder="Name"
value=name
class=(if isValidName 'valid' '')}}
</p>
<p>
{{input name="url"
type="text"
placeholder="URL"
value=url
class=(if isValidURL 'valid' '')}}
</p>
<p>
{{input name="github_uid"
type="text"
placeholder="GitHub UID (123)"
value=github_uid
class=(if isValidGithubUID 'valid' '')}}
</p>
<p>
{{input name="github_username"
type="text"
placeholder="GitHub username"
value=github_username
class=(if isValidGithubUsername 'valid' '')}}
</p>
<p>
{{input name="wiki_username"
type="text"
placeholder="Wiki Username"
value=wiki_username
class=(if isValidWikiUsername 'valid' '')}}
</p> </p>
<p class="actions"> <p class="actions">
<input type="submit" value="Save"> {{input type="submit"
disabled=(is-pending inProgress)
value=(if (is-pending inProgress) 'Processing' 'Save')}}
</p> </p>
</form> </form>
+57
View File
@@ -0,0 +1,57 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { and, notEmpty } from '@ember/object/computed';
export default Component.extend({
// Default attributes used by reset
attributes: {
contributorId: null,
kind: 'community',
amount: null,
description: null,
url: null,
},
didInsertElement() {
this._super(...arguments);
this.reset();
},
contributors: [],
isValidContributor: notEmpty('contributorId'),
isValidAmount: computed('amount', function() {
return parseInt(this.amount, 10) > 0;
}),
isValidDescription: notEmpty('description'),
isValidUrl: notEmpty('url'),
isValid: and('isValidContributor',
'isValidAmount',
'isValidDescription'),
reset: function() {
this.setProperties(this.attributes);
},
actions: {
submit() {
if (!this.isValid) {
alert('Invalid data. Please review and try again.');
return;
}
let attributes = Object.keys(this.attributes);
let proposal = this.getProperties(attributes);
let saved = this.save(proposal);
// The promise handles inProgress
this.set('inProgress', saved);
saved.then(() => {
this.reset();
window.scroll(0,0);
window.alert('Proposal added.');
});
}
}
});
+44
View File
@@ -0,0 +1,44 @@
<form {{action "submit" on="submit"}}>
<p>
<select required onchange={{action (mut contributorId) value="target.value"}}>
<option value="" selected disabled hidden>Contributor</option>
{{#each contributors as |contributor|}}
<option value={{contributor.id}} selected={{eq contributorId contributor.id}}>{{contributor.github_username}}</option>
{{/each}}
</select>
</p>
<p>
<select required onchange={{action (mut kind) value="target.value"}}>
<option value="community" selected={{eq kind "community"}}>Community</option>
<option value="design" selected={{eq kind "design"}}>Design</option>
<option value="dev" selected={{eq kind "dev"}}>Development</option>
<option value="docs" selected={{eq kind "docs"}}>Documentation</option>
<option value="ops" selected={{eq kind "ops"}}>IT Operations</option>
</select>
</p>
<p>
{{input type="text"
placeholder="100"
value=amount
class=(if isValidAmount 'valid' '')}}
</p>
<p>
{{input type="text"
placeholder="Description"
value=description
class=(if isValidDescription 'valid' '')}}
</p>
<p>
{{input type="text"
placeholder="URL (optional)"
value=url
class=(if isValidUrl 'valid' '')}}
</p>
<p class="actions">
{{input type="submit"
disabled=(is-pending inProgress)
value=(if (is-pending inProgress) 'Processing' 'Save')}}
{{#link-to 'index'}}Back{{/link-to}}
</p>
</form>
+17 -3
View File
@@ -1,8 +1,22 @@
import Ember from 'ember'; import Component from '@ember/component';
export default Ember.Component.extend({ export default Component.extend({
tagName: 'table', tagName: 'table',
classNames: 'contributor-list' classNames: 'contributor-list',
selectedContributor: null,
actions: {
toggleContributorInfo(contributor) {
if (contributor.get('showMetadata')) {
contributor.set('showMetadata', false);
} else {
this.contributors.setEach('showMetadata', false);
contributor.set('showMetadata', true);
}
}
}
}); });
+16 -3
View File
@@ -1,14 +1,27 @@
<tbody> <tbody>
{{#each contributors as |contributor|}} {{#each contributors as |contributor|}}
<tr> <tr class="{{if contributor.isCurrentUser 'current-user'}}" {{action "toggleContributorInfo" contributor}}>
<td class="person"> <td class="person">
<img class="avatar" src={{contributor.avatarURL}}> <img class="avatar" src={{contributor.avatarURL}}>
{{contributor.github_username}} {{contributor.name}}
</td> </td>
<td class="kredits"> <td class="kredits">
<span class="amount">{{contributor.kredits}}</span> <span class="amount">{{contributor.balance}}</span>
<span class="symbol">₭S</span> <span class="symbol">₭S</span>
</td> </td>
</tr> </tr>
<tr class="metadata {{if contributor.isCurrentUser 'current-user'}} {{if contributor.showMetadata 'visible'}}">
<td colspan="2">
<ul>
<li><a href="https://testnet.etherscan.io/address/{{contributor.account}}">Inspect Ethereum transactions</a></li>
{{#if contributor.ipfsHash}}
<li><a href="https://ipfs.io/ipfs/{{contributor.ipfsHash}}">Inspect IPFS profile</a></li>
{{/if}}
</ul>
{{#if contributor.showMetadata}}
<pre>{{contributor.ipfsData}}</pre>
{{/if}}
</td>
</tr>
{{/each}} {{/each}}
</tbody> </tbody>
+2 -2
View File
@@ -1,6 +1,6 @@
import Ember from 'ember'; import Component from '@ember/component';
export default Ember.Component.extend({ export default Component.extend({
classNames: ['loading-spinner'] classNames: ['loading-spinner']
+7 -3
View File
@@ -1,6 +1,6 @@
import Ember from 'ember'; import Component from '@ember/component';
export default Ember.Component.extend({ export default Component.extend({
tagName: 'ul', tagName: 'ul',
classNames: ['proposal-list'], classNames: ['proposal-list'],
@@ -8,7 +8,11 @@ export default Ember.Component.extend({
actions: { actions: {
confirm(proposalId) { confirm(proposalId) {
this.sendAction('confirmAction', proposalId); if (this.contractInteractionEnabled) {
this.confirmProposal(proposalId);
} else {
window.alert('Only members can vote on proposals. Please ask someone to set you up.');
}
} }
} }
+6 -5
View File
@@ -1,11 +1,12 @@
{{#each proposals as |proposal|}} {{#each proposals as |proposal|}}
<li> <li data-proposal-id={{proposal.id}} title="({{proposal.kind}}) {{proposal.description}}">
<span class="id">#{{proposal.id}}</span> <span class="category {{proposal.kind}}">♥</span>
Issue{{#if proposal.executed}}d{{/if}}
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span> <span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
to <span class="recipient" title="{{proposal.recipientAddress}}">{{proposal.recipientName}}</span> for <span class="recipient">{{proposal.contributor.name}}</span>
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span> <span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
{{#unless proposal.executed}}<button {{action "confirm" proposal.id}}>+1</button>{{/unless}} {{#unless proposal.isExecuted}}
<button {{action "confirm" proposal.id}}>+1</button>
{{/unless}}
</li> </li>
{{/each}} {{/each}}
+2 -2
View File
@@ -1,4 +1,4 @@
import Ember from 'ember'; import Controller from '@ember/controller';
export default Ember.Controller.extend({ export default Controller.extend({
}); });
+26 -47
View File
@@ -1,58 +1,37 @@
import Ember from 'ember'; import Controller from '@ember/controller';
import { alias, filter, filterBy, sort } from '@ember/object/computed';
import { inject as injectService } from '@ember/service';
const { export default Controller.extend({
computed, kredits: injectService(),
inject: {
service
}
} = Ember;
export default Ember.Controller.extend({ contributors: alias('kredits.contributors'),
contributorsWithKredits: filter('contributors', function(contributor) {
kredits: service(), return contributor.get('balance') !== 0;
}),
contractInteractionEnabled: computed.alias('kredits.web3Provided'), contributorsSorting: ['balance:desc'],
contributorsSorted: sort('contributorsWithKredits', 'contributorsSorting'),
findContributorByAddress(address) {
return this.get('model.contributors')
.findBy('address', address);
},
proposalsOpen: function() {
let proposals = this.get('model.proposals')
.filterBy('executed', false)
.map(p => {
p.recipientName = this.findContributorByAddress(p.recipientAddress).github_username;
return p;
});
return proposals;
}.property('model.proposals.[]', 'model.contributors.[]'),
proposalsClosed: function() {
let proposals = this.get('model.proposals')
.filterBy('executed', true)
.map(p => {
p.recipientName = this.findContributorByAddress(p.recipientAddress).github_username;
return p;
});
return proposals;
}.property('model.proposals.[]', 'model.contributors.[]'),
proposals: alias('kredits.proposals'),
proposalsOpen: filterBy('proposals', 'isExecuted', false),
proposalsClosed: filterBy('proposals', 'isExecuted', true),
proposalsSorting: ['id:desc'], proposalsSorting: ['id:desc'],
proposalsClosedSorted: Ember.computed.sort('proposalsClosed', 'proposalsSorting'), proposalsClosedSorted: sort('proposalsClosed', 'proposalsSorting'),
proposalsOpenSorted: Ember.computed.sort('proposalsOpen', 'proposalsSorting'), proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'),
contributorsSorting: ['kredits:desc'],
contributorsSorted: Ember.computed.sort('model.contributors', 'contributorsSorting'),
actions: { actions: {
confirmProposal(proposalId) { confirmProposal(proposalId) {
this.get('kredits').vote(proposalId).then(transactionId => { this.kredits.vote(proposalId).then(transaction => {
window.confirm('Vote submitted to Ethereum blockhain: '+transactionId); window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
}); });
},
save(contributor) {
return this.kredits.addContributor(contributor)
.then((contributor) => {
this.contributors.pushObject(contributor);
return contributor;
});
} }
} }
}); });
+24
View File
@@ -0,0 +1,24 @@
import Controller from '@ember/controller';
import { alias, filterBy } from '@ember/object/computed';
import { inject as injectService } from '@ember/service';
export default Controller.extend({
kredits: injectService(),
contributors: alias('kredits.contributors'),
minedContributors: filterBy('contributors', 'id'),
actions: {
save(proposal) {
// contributorIpfsHash is needed for the proposal ipfs data. I'm not happy to do this here but I think to load all the contributors in addProposal again is a bit too much. I hope we can refactor it later.
let contributor = this.contributors.findBy('id', proposal.contributorId);
proposal.contributorIpfsHash = contributor.get('ipfsHash');
return this.kredits.addProposal(proposal)
.then((proposal) => {
this.transitionToRoute('index');
return proposal;
});
}
}
});
+9 -3
View File
@@ -3,16 +3,22 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>KreditsWeb</title> <title>Kosmos Kredits</title>
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
{{content-for "head"}} {{content-for "head"}}
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css"> <link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link rel="stylesheet" href="{{rootURL}}assets/kredits-web.css"> <link integrity="" rel="stylesheet" href="{{rootURL}}assets/kredits-web.css">
{{content-for "head-footer"}} {{content-for "head-footer"}}
<script>
window.addEventListener('load', function() {
window.windowLoadComplete = true;
});
</script>
</head> </head>
<body> <body>
{{content-for "body"}} {{content-for "body"}}
+24 -7
View File
@@ -1,14 +1,31 @@
import Ember from 'ember'; import { computed } from '@ember/object';
import EmberObject from '@ember/object';
import bignumber from 'kredits-web/utils/cps/bignumber';
export default Ember.Object.extend({ export default EmberObject.extend({
// Contract
id: bignumber('idRaw', 'toString'),
account: null,
balance: bignumber('balanceRaw', 'toNumber'),
isCore: false,
ipfsHash: null,
// IPFS
kind: null,
name: null,
url: null,
github_username: null, github_username: null,
github_uid: null, github_uid: null,
kredits: null, wiki_username: null,
address: null, ipfsData: '',
avatarURL: function() { // Deprecated
return `https\:\/\/avatars2.githubusercontent.com/u/${this.get('github_uid')}?v=3&s=128`; isCurrentUser: false,
}.property('github_uid')
avatarURL: computed('github_uid', function() {
let github_uid = this.github_uid;
if (github_uid) {
return `https://avatars2.githubusercontent.com/u/${github_uid}?v=3&s=128`;
}
}),
}); });
+21 -11
View File
@@ -1,15 +1,25 @@
import Ember from 'ember'; import EmberObject from '@ember/object';
import { alias } from '@ember/object/computed';
import bignumber from 'kredits-web/utils/cps/bignumber';
export default Ember.Object.extend({ export default EmberObject.extend({
// Contract
id: null, id: bignumber('idRaw', 'toString'),
creator: null, creatorAccount: null,
recipient: null, contributorId: bignumber('contributorIdRaw', 'toString'),
votesCount: null, amount: bignumber('amountRaw', 'toNumber'),
votesNeeded: null, votesCount: bignumber('votesCountRaw', 'toNumber'),
amount: null, votesNeeded: bignumber('votesNeededRaw', 'toNumber'),
executed: null, executed: null,
url: null, ipfsHash: null,
ipfsHash: null
// Shortcuts
isExecuted: alias('executed'),
// IPFS
kind: null,
description: null,
details: {},
url: null,
ipfsData: '',
}); });
+5 -2
View File
@@ -1,13 +1,16 @@
import Ember from 'ember'; import EmberRouter from '@ember/routing/router';
import config from './config/environment'; import config from './config/environment';
const Router = Ember.Router.extend({ const Router = EmberRouter.extend({
location: config.locationType, location: config.locationType,
rootURL: config.rootURL rootURL: config.rootURL
}); });
Router.map(function() { Router.map(function() {
this.route('spinner'); this.route('spinner');
this.route('proposals', function() {
this.route('new');
});
}); });
export default Router; export default Router;
+25 -2
View File
@@ -1,4 +1,27 @@
import Ember from 'ember'; import { inject as injectService } from '@ember/service';
import Route from '@ember/routing/route';
export default Ember.Route.extend({ export default Route.extend({
kredits: injectService(),
beforeModel(transition) {
const kredits = this.kredits;
return kredits.setup().then(() => {
if (kredits.get('accountNeedsUnlock')) {
if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) {
transition.retry();
}
}
}).catch((error) => {
console.log('Error initializing Kredits', error);
});
},
afterModel() {
return this.kredits.loadContributorsAndProposals()
.then(() => {
this.kredits.addContractEventHandlers();
});
}
}); });
-18
View File
@@ -1,18 +0,0 @@
import Ember from 'ember';
export default Ember.Route.extend({
kredits: Ember.inject.service(),
model() {
let kredits = this.get('kredits');
return Ember.RSVP.hash({
contributors: kredits.getContributors(),
totalSupply: kredits.getValueFromContract('totalSupply'),
contributorsCount: kredits.getValueFromContract('contributorsCount'),
proposals: kredits.getProposals()
});
}
});
+2 -2
View File
@@ -1,4 +1,4 @@
import Ember from 'ember'; import Route from '@ember/routing/route';
export default Ember.Route.extend({ export default Route.extend({
}); });
+194 -107
View File
@@ -1,134 +1,221 @@
import Ember from 'ember'; import ethers from 'npm:ethers';
import Web3 from 'npm:web3'; import Kredits from 'npm:kredits-contracts';
import RSVP from 'rsvp';
import Service from '@ember/service';
import { computed } from '@ember/object';
import { alias, notEmpty } from '@ember/object/computed';
import { isEmpty } from '@ember/utils';
import config from 'kredits-web/config/environment'; import config from 'kredits-web/config/environment';
import Contributor from 'kredits-web/models/contributor'; import Contributor from 'kredits-web/models/contributor'
import Proposal from 'kredits-web/models/proposal'; import Proposal from 'kredits-web/models/proposal'
export default Ember.Service.extend({
web3Instance: null, export default Service.extend({
web3Provided: false, // Web3 provided (using Mist Browser, Metamask et al.)
web3: function() { currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
if (Ember.isPresent(this.get('web3Instance'))) { currentUser: null,
return this.get('web3Instance'); currentUserIsContributor: notEmpty('currentUser'),
} currentUserIsCore: alias('currentUser.isCore'),
hasAccounts: notEmpty('currentUserAccounts'),
accountNeedsUnlock: computed('currentUserAccounts', function() {
return this.currentUserAccounts && isEmpty(this.currentUserAccounts);
}),
let web3Instance; // this is called in the routes beforeModel(). So it is initialized before everything else
// and we can rely on the ethProvider and the potential currentUserAccounts to be available
if (typeof window.web3 !== 'undefined') { getEthProvider: function() {
Ember.Logger.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask'); return new RSVP.Promise((resolve) => {
web3Instance = window.web3; let ethProvider;
this.set('web3Provided', true); let networkId;
} else { let networkName;
Ember.Logger.debug('[kredits] Creating new instance from npm module class'); if (typeof window.web3 !== 'undefined') {
let provider = new Web3.providers.HttpProvider("http://139.59.248.169:8545"); console.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
web3Instance = new Web3(provider); networkId = parseInt(window.web3.version.network);
} ethProvider = new ethers.providers.Web3Provider(
window.web3.currentProvider,
this.set('web3Instance', web3Instance); { chainId: networkId }
window.web3 = web3Instance; );
ethProvider.listAccounts().then((accounts) => {
return web3Instance; this.set('currentUserAccounts', accounts);
}.property('web3Instance'), resolve(ethProvider, ethProvider.getSigner());
});
kreditsContract: function() { } else {
// TODO cache this console.debug('[kredits] Creating new instance from npm module class');
let contract = this.get('web3') networkId = parseInt(config.contractMetadata.networkId);
.eth.contract(config.kreditsContract.ABI) networkName = config.contractMetadata.networkName;
.at(config.kreditsContract.address); console.log(networkName);
window.Kredits = contract; ethProvider = new ethers.providers.InfuraProvider({ chainId: networkId, name: networkName });
return contract; resolve(ethProvider, null);
}.property('web3'), }
getValueFromContract(contractMethod, ...args) {
return new Ember.RSVP.Promise((resolve, reject) => {
this.get('kreditsContract')[contractMethod](...args, (err, data) => {
if (err) { reject(err); }
resolve(data);
});
}); });
}, },
getContributorData(i) { setup() {
let promise = new Ember.RSVP.Promise((resolve, reject) => { return this.getEthProvider().then((ethProvider, ethSigner) => {
this.getValueFromContract('contributorAddresses', i).then(address => { let kredits = new Kredits(ethProvider, ethSigner);
this.getValueFromContract('contributors', address).then(person => { return kredits
this.getValueFromContract('balanceOf', address).then(balance => { .init()
let contributor = Contributor.create({ .then((kredits) => {
address: address, this.set('kredits', kredits);
github_username: person[1],
github_uid: person[0], if (this.currentUserAccounts && this.currentUserAccounts.length > 0) {
ipfsHash: person[3], this.getCurrentUser.then((contributorData) => {
kredits: balance.toNumber() this.set('currentUser', contributorData);
}); });
Ember.Logger.debug('[kredits] contributor', contributor); }
resolve(contributor); return kredits;
});
}); });
}).catch(err => reject(err));
}); });
return promise; },
totalSupply: computed(function() {
return this.kredits.Token.functions.totalSupply();
}),
contributors: [],
proposals: [],
loadContributorsAndProposals() {
return this.getContributors()
.then(contributors => this.contributors.pushObjects(contributors))
.then(() => this.getProposals())
.then(proposals => this.proposals.pushObjects(proposals))
},
addContributor(attributes) {
console.debug('[kredits] add contributor', attributes);
return this.kredits.Contributor.add(attributes)
.then((data) => {
console.debug('[kredits] add contributor response', data);
return Contributor.create(attributes);
});
}, },
getContributors() { getContributors() {
return this.getValueFromContract('contributorsCount').then(contributorsCount => { return this.kredits.Contributor.all()
let contributors = []; .then((contributors) => {
return contributors.map((contributor) => {
for(var i = 0; i < contributorsCount.toNumber(); i++) { return Contributor.create(contributor);
contributors.push(this.getContributorData(i)); });
} });
return Ember.RSVP.all(contributors);
});
}, },
getProposalData(i) { addProposal(attributes) {
let promise = new Ember.RSVP.Promise((resolve, reject) => { console.debug('[kredits] add proposal', attributes);
this.getValueFromContract('proposals', i).then(p => {
let proposal = Proposal.create({ return this.kredits.Operator.addProposal(attributes)
id : i, .then((data) => {
creatorAddress : p[0], console.debug('[kredits] add proposal response', data);
recipientAddress : p[1], attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
votesCount : p[2].toNumber(), return Proposal.create(attributes);
votesNeeded : p[3].toNumber(), });
amount : p[4].toNumber(),
executed : p[5],
url : p[6],
ipfsHash : p[7]
});
Ember.Logger.debug('[kredits] proposal', proposal);
resolve(proposal);
}).catch(err => reject(err));
});
return promise;
}, },
getProposals() { getProposals() {
return this.getValueFromContract('proposalsCount').then(proposalsCount => { return this.kredits.Operator.all()
let proposals = []; .then((proposals) => {
return proposals.map((proposal) => {
for(var i = 0; i < proposalsCount.toNumber(); i++) { proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString());
proposals.push(this.getProposalData(i)); return Proposal.create(proposal);
} });
});
return Ember.RSVP.all(proposals);
});
}, },
vote(proposalId) { vote(proposalId) {
Ember.Logger.debug('[kredits] vote for', proposalId); console.debug('[kredits] vote for', proposalId);
return new Ember.RSVP.Promise((resolve, reject) => {
this.get('kreditsContract').vote(proposalId, (err, data) => { return this.kredits.Operator.functions.vote(proposalId)
if (err) { reject(err); } .then((data) => {
Ember.Logger.debug('[kredits] vote response', data); console.debug('[kredits] vote response', data);
resolve(data); return data;
}); });
});
}, },
logKreditsContract: function() { getCurrentUser: computed('kredits.provider', function() {
Ember.Logger.debug('[kredits] kreditsContract', this.get('kreditsContract')); if (isEmpty(this.currentUserAccounts)) {
}.on('init') return RSVP.resolve();
}
return this.kredits.Contributor
.functions.getContributorIdByAddress(this.get('currentUserAccounts.firstObject'))
.then((id) => {
id = id.toNumber();
// check if the user is a contributor or not
if (id === 0) {
return RSVP.resolve();
} else {
return this.kredits.Contributor.getById(id);
}
});
}),
findProposalById(proposalId) {
return this.proposals.findBy('id', proposalId.toString());
},
// Contract events
addContractEventHandlers() {
// Operator events
this.kredits.Operator
.on('ProposalCreated', this.handleProposalCreated.bind(this))
.on('ProposalVoted', this.handleProposalVoted.bind(this))
.on('ProposalExecuted', this.handleProposalExecuted.bind(this));
// Token events
this.kredits.Token
.on('Transfer', this.handleTransfer.bind(this));
},
handleProposalCreated(proposalId) {
let proposal = this.findProposalById(proposalId);
if (proposal) {
console.debug('[events] proposal exists, not adding from event');
return;
}
this.kredits.Operator.getById(proposalId)
.then((proposal) => {
proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString());
this.proposals.pushObject(Proposal.create(proposal));
});
},
// TODO: We may want to reload that proposal to show the voter as voted
handleProposalVoted(proposalId, voterId, totalVotes) {
let proposal = this.findProposalById(proposalId);
if (proposal) {
proposal.set('votesCount', totalVotes);
}
},
handleProposalExecuted(proposalId, contributorId, amount) {
let proposal = this.findProposalById(proposalId);
if (proposal.get('isExecuted')) {
console.debug('[events] proposal already executed, not adding from event');
return;
}
proposal.set('executed', true);
this.contributors
.findBy('id', contributorId.toString())
.incrementProperty('balance', amount.toNumber());
},
handleTransfer(from, to, value) {
value = value.toNumber();
this.contributors
.findBy('address', from)
.decrementProperty('balance', value);
this.contributors
.findBy('address', to)
.incrementProperty('balance', value);
},
}); });
+8
View File
@@ -0,0 +1,8 @@
$blue: #68d7fb;
$purple: #8f68fb;
$pink: #e068fb;
$green: #97fb68;
$yellow: #fbe468;
$red: #fb6868;
$primaryColor: $blue;
+23 -6
View File
@@ -10,11 +10,12 @@
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
@import "bourbon"; @import "bourbon/bourbon";
@import "settings/neat"; @import "settings/neat";
@import "neat"; @import "neat/neat";
@import "settings/breakpoints"; @import "settings/breakpoints";
@import "layout"; @import "layout";
@import "colors";
$font-family-sans: 'Open Sans', sans-serif; $font-family-sans: 'Open Sans', sans-serif;
@@ -23,7 +24,7 @@ body {
background-repeat: none; background-repeat: none;
background-attachment: fixed; background-attachment: fixed;
font-family: $font-family-sans; font-family: $font-family-sans;
font-size: 16px; font-size: 14px;
font-weight: 300; font-weight: 300;
color: #fff; color: #fff;
} }
@@ -43,7 +44,7 @@ h1, h2, h3, h4, h5, input, button {
section { section {
h2 { h2 {
font-size: 2.8rem; font-size: 2.8rem;
color: lightblue; color: $primaryColor;
@include media($mobile) { @include media($mobile) {
font-size: 2rem; font-size: 2rem;
} }
@@ -54,7 +55,7 @@ section {
p.stats { p.stats {
padding-top: 3rem; padding-top: 3rem;
font-size: 1rem; font-size: 1rem;
color: lightblue; color: white;
text-align: center; text-align: center;
span.number { span.number {
font-weight: 600; font-weight: 600;
@@ -65,13 +66,29 @@ section {
} }
} }
} }
&#proposals-open, &#proposals-closed {
.actions {
padding-top: 3rem;
font-size: 1rem;
color: $primaryColor;
text-align: center;
@include media($mobile) {
padding-top: 2rem;
}
a {
color: $primaryColor;
}
}
}
} }
button, input[type=submit] { button, input[type=submit] {
display: inline-block; display: inline-block;
border: 1px solid rgba(22, 21, 40, 1); border: 1px solid rgba(22, 21, 40, 1);
background-color: rgba(22, 21, 40, 0.6); background-color: rgba(22, 21, 40, 0.6);
color: lightblue; color: $primaryColor;
border-radius: 3px; border-radius: 3px;
font-weight: 500; font-weight: 500;
text-transform: uppercase; text-transform: uppercase;
+19
View File
@@ -0,0 +1,19 @@
@charset "UTF-8";
/// Throws Sass warnings to announce library deprecations. You can disable them
/// by setting the `$output-bourbon-deprecation-warnings` variable to `false`.
///
/// @access private
@mixin _bourbon-deprecate($feature, $message: null) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `#{$feature}` is deprecated and will be " +
"removed in 5.0.0. #{$message}";
@content;
}
}
@mixin _bourbon-deprecate-for-prefixing($feature) {
@include _bourbon-deprecate($feature, "We suggest using an automated " +
"prefixing tool, like Autoprefixer.");
}
+425
View File
@@ -0,0 +1,425 @@
// The following features have been deprecated and will be removed in the next MAJOR version release
@mixin inline-block {
@include _bourbon-deprecate("inline-block");
display: inline-block;
}
@mixin button ($style: simple, $base-color: #4294f0, $text-size: inherit, $padding: 7px 18px) {
@include _bourbon-deprecate("button");
@if type-of($style) == string and type-of($base-color) == color {
@include buttonstyle($style, $base-color, $text-size, $padding);
}
@if type-of($style) == string and type-of($base-color) == number {
$padding: $text-size;
$text-size: $base-color;
$base-color: #4294f0;
@if $padding == inherit {
$padding: 7px 18px;
}
@include buttonstyle($style, $base-color, $text-size, $padding);
}
@if type-of($style) == color and type-of($base-color) == color {
$base-color: $style;
$style: simple;
@include buttonstyle($style, $base-color, $text-size, $padding);
}
@if type-of($style) == color and type-of($base-color) == number {
$padding: $text-size;
$text-size: $base-color;
$base-color: $style;
$style: simple;
@if $padding == inherit {
$padding: 7px 18px;
}
@include buttonstyle($style, $base-color, $text-size, $padding);
}
@if type-of($style) == number {
$padding: $base-color;
$text-size: $style;
$base-color: #4294f0;
$style: simple;
@if $padding == #4294f0 {
$padding: 7px 18px;
}
@include buttonstyle($style, $base-color, $text-size, $padding);
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
}
// Selector Style Button
@mixin buttonstyle($type, $b-color, $t-size, $pad) {
// Grayscale button
@if $type == simple and $b-color == grayscale($b-color) {
@include simple($b-color, true, $t-size, $pad);
}
@if $type == shiny and $b-color == grayscale($b-color) {
@include shiny($b-color, true, $t-size, $pad);
}
@if $type == pill and $b-color == grayscale($b-color) {
@include pill($b-color, true, $t-size, $pad);
}
@if $type == flat and $b-color == grayscale($b-color) {
@include flat($b-color, true, $t-size, $pad);
}
// Colored button
@if $type == simple {
@include simple($b-color, false, $t-size, $pad);
}
@else if $type == shiny {
@include shiny($b-color, false, $t-size, $pad);
}
@else if $type == pill {
@include pill($b-color, false, $t-size, $pad);
}
@else if $type == flat {
@include flat($b-color, false, $t-size, $pad);
}
}
// Simple Button
@mixin simple($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
$color: hsl(0, 0, 100%);
$border: adjust-color($base-color, $saturation: 9%, $lightness: -14%);
$inset-shadow: adjust-color($base-color, $saturation: -8%, $lightness: 15%);
$stop-gradient: adjust-color($base-color, $saturation: 9%, $lightness: -11%);
$text-shadow: adjust-color($base-color, $saturation: 15%, $lightness: -18%);
@if is-light($base-color) {
$color: hsl(0, 0, 20%);
$text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
}
@if $grayscale == true {
$border: grayscale($border);
$inset-shadow: grayscale($inset-shadow);
$stop-gradient: grayscale($stop-gradient);
$text-shadow: grayscale($text-shadow);
}
border: 1px solid $border;
border-radius: 3px;
box-shadow: inset 0 1px 0 0 $inset-shadow;
color: $color;
display: inline-block;
font-size: $textsize;
font-weight: bold;
@include linear-gradient ($base-color, $stop-gradient);
padding: $padding;
text-decoration: none;
text-shadow: 0 1px 0 $text-shadow;
background-clip: padding-box;
&:hover:not(:disabled) {
$base-color-hover: adjust-color($base-color, $saturation: -4%, $lightness: -5%);
$inset-shadow-hover: adjust-color($base-color, $saturation: -7%, $lightness: 5%);
$stop-gradient-hover: adjust-color($base-color, $saturation: 8%, $lightness: -14%);
@if $grayscale == true {
$base-color-hover: grayscale($base-color-hover);
$inset-shadow-hover: grayscale($inset-shadow-hover);
$stop-gradient-hover: grayscale($stop-gradient-hover);
}
@include linear-gradient ($base-color-hover, $stop-gradient-hover);
box-shadow: inset 0 1px 0 0 $inset-shadow-hover;
cursor: pointer;
}
&:active:not(:disabled),
&:focus:not(:disabled) {
$border-active: adjust-color($base-color, $saturation: 9%, $lightness: -14%);
$inset-shadow-active: adjust-color($base-color, $saturation: 7%, $lightness: -17%);
@if $grayscale == true {
$border-active: grayscale($border-active);
$inset-shadow-active: grayscale($inset-shadow-active);
}
border: 1px solid $border-active;
box-shadow: inset 0 0 8px 4px $inset-shadow-active, inset 0 0 8px 4px $inset-shadow-active;
}
}
// Shiny Button
@mixin shiny($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
$color: hsl(0, 0, 100%);
$border: adjust-color($base-color, $red: -117, $green: -111, $blue: -81);
$border-bottom: adjust-color($base-color, $red: -126, $green: -127, $blue: -122);
$fourth-stop: adjust-color($base-color, $red: -79, $green: -70, $blue: -46);
$inset-shadow: adjust-color($base-color, $red: 37, $green: 29, $blue: 12);
$second-stop: adjust-color($base-color, $red: -56, $green: -50, $blue: -33);
$text-shadow: adjust-color($base-color, $red: -140, $green: -141, $blue: -114);
$third-stop: adjust-color($base-color, $red: -86, $green: -75, $blue: -48);
@if is-light($base-color) {
$color: hsl(0, 0, 20%);
$text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
}
@if $grayscale == true {
$border: grayscale($border);
$border-bottom: grayscale($border-bottom);
$fourth-stop: grayscale($fourth-stop);
$inset-shadow: grayscale($inset-shadow);
$second-stop: grayscale($second-stop);
$text-shadow: grayscale($text-shadow);
$third-stop: grayscale($third-stop);
}
@include linear-gradient(top, $base-color 0%, $second-stop 50%, $third-stop 50%, $fourth-stop 100%);
border: 1px solid $border;
border-bottom: 1px solid $border-bottom;
border-radius: 5px;
box-shadow: inset 0 1px 0 0 $inset-shadow;
color: $color;
display: inline-block;
font-size: $textsize;
font-weight: bold;
padding: $padding;
text-align: center;
text-decoration: none;
text-shadow: 0 -1px 1px $text-shadow;
&:hover:not(:disabled) {
$first-stop-hover: adjust-color($base-color, $red: -13, $green: -15, $blue: -18);
$second-stop-hover: adjust-color($base-color, $red: -66, $green: -62, $blue: -51);
$third-stop-hover: adjust-color($base-color, $red: -93, $green: -85, $blue: -66);
$fourth-stop-hover: adjust-color($base-color, $red: -86, $green: -80, $blue: -63);
@if $grayscale == true {
$first-stop-hover: grayscale($first-stop-hover);
$second-stop-hover: grayscale($second-stop-hover);
$third-stop-hover: grayscale($third-stop-hover);
$fourth-stop-hover: grayscale($fourth-stop-hover);
}
@include linear-gradient(top, $first-stop-hover 0%,
$second-stop-hover 50%,
$third-stop-hover 50%,
$fourth-stop-hover 100%);
cursor: pointer;
}
&:active:not(:disabled),
&:focus:not(:disabled) {
$inset-shadow-active: adjust-color($base-color, $red: -111, $green: -116, $blue: -122);
@if $grayscale == true {
$inset-shadow-active: grayscale($inset-shadow-active);
}
box-shadow: inset 0 0 20px 0 $inset-shadow-active;
}
}
// Pill Button
@mixin pill($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
$color: hsl(0, 0, 100%);
$border-bottom: adjust-color($base-color, $hue: 8, $saturation: -11%, $lightness: -26%);
$border-sides: adjust-color($base-color, $hue: 4, $saturation: -21%, $lightness: -21%);
$border-top: adjust-color($base-color, $hue: -1, $saturation: -30%, $lightness: -15%);
$inset-shadow: adjust-color($base-color, $hue: -1, $saturation: -1%, $lightness: 7%);
$stop-gradient: adjust-color($base-color, $hue: 8, $saturation: 14%, $lightness: -10%);
$text-shadow: adjust-color($base-color, $hue: 5, $saturation: -19%, $lightness: -15%);
@if is-light($base-color) {
$color: hsl(0, 0, 20%);
$text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
}
@if $grayscale == true {
$border-bottom: grayscale($border-bottom);
$border-sides: grayscale($border-sides);
$border-top: grayscale($border-top);
$inset-shadow: grayscale($inset-shadow);
$stop-gradient: grayscale($stop-gradient);
$text-shadow: grayscale($text-shadow);
}
border: 1px solid $border-top;
border-color: $border-top $border-sides $border-bottom;
border-radius: 16px;
box-shadow: inset 0 1px 0 0 $inset-shadow;
color: $color;
display: inline-block;
font-size: $textsize;
font-weight: normal;
line-height: 1;
@include linear-gradient ($base-color, $stop-gradient);
padding: $padding;
text-align: center;
text-decoration: none;
text-shadow: 0 -1px 1px $text-shadow;
background-clip: padding-box;
&:hover:not(:disabled) {
$base-color-hover: adjust-color($base-color, $lightness: -4.5%);
$border-bottom: adjust-color($base-color, $hue: 8, $saturation: 13.5%, $lightness: -32%);
$border-sides: adjust-color($base-color, $hue: 4, $saturation: -2%, $lightness: -27%);
$border-top: adjust-color($base-color, $hue: -1, $saturation: -17%, $lightness: -21%);
$inset-shadow-hover: adjust-color($base-color, $saturation: -1%, $lightness: 3%);
$stop-gradient-hover: adjust-color($base-color, $hue: 8, $saturation: -4%, $lightness: -15.5%);
$text-shadow-hover: adjust-color($base-color, $hue: 5, $saturation: -5%, $lightness: -22%);
@if $grayscale == true {
$base-color-hover: grayscale($base-color-hover);
$border-bottom: grayscale($border-bottom);
$border-sides: grayscale($border-sides);
$border-top: grayscale($border-top);
$inset-shadow-hover: grayscale($inset-shadow-hover);
$stop-gradient-hover: grayscale($stop-gradient-hover);
$text-shadow-hover: grayscale($text-shadow-hover);
}
@include linear-gradient ($base-color-hover, $stop-gradient-hover);
background-clip: padding-box;
border: 1px solid $border-top;
border-color: $border-top $border-sides $border-bottom;
box-shadow: inset 0 1px 0 0 $inset-shadow-hover;
cursor: pointer;
text-shadow: 0 -1px 1px $text-shadow-hover;
}
&:active:not(:disabled),
&:focus:not(:disabled) {
$active-color: adjust-color($base-color, $hue: 4, $saturation: -12%, $lightness: -10%);
$border-active: adjust-color($base-color, $hue: 6, $saturation: -2.5%, $lightness: -30%);
$border-bottom-active: adjust-color($base-color, $hue: 11, $saturation: 6%, $lightness: -31%);
$inset-shadow-active: adjust-color($base-color, $hue: 9, $saturation: 2%, $lightness: -21.5%);
$text-shadow-active: adjust-color($base-color, $hue: 5, $saturation: -12%, $lightness: -21.5%);
@if $grayscale == true {
$active-color: grayscale($active-color);
$border-active: grayscale($border-active);
$border-bottom-active: grayscale($border-bottom-active);
$inset-shadow-active: grayscale($inset-shadow-active);
$text-shadow-active: grayscale($text-shadow-active);
}
background: $active-color;
border: 1px solid $border-active;
border-bottom: 1px solid $border-bottom-active;
box-shadow: inset 0 0 6px 3px $inset-shadow-active;
text-shadow: 0 -1px 1px $text-shadow-active;
}
}
// Flat Button
@mixin flat($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
$color: hsl(0, 0, 100%);
@if is-light($base-color) {
$color: hsl(0, 0, 20%);
}
background-color: $base-color;
border-radius: 3px;
border: 0;
color: $color;
display: inline-block;
font-size: $textsize;
font-weight: bold;
padding: $padding;
text-decoration: none;
background-clip: padding-box;
&:hover:not(:disabled){
$base-color-hover: adjust-color($base-color, $saturation: 4%, $lightness: 5%);
@if $grayscale == true {
$base-color-hover: grayscale($base-color-hover);
}
background-color: $base-color-hover;
cursor: pointer;
}
&:active:not(:disabled),
&:focus:not(:disabled) {
$base-color-active: adjust-color($base-color, $saturation: -4%, $lightness: -5%);
@if $grayscale == true {
$base-color-active: grayscale($base-color-active);
}
background-color: $base-color-active;
cursor: pointer;
}
}
// Flexible grid
@function flex-grid($columns, $container-columns: $fg-max-columns) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `flex-grid` is deprecated and will be " +
"removed in 5.0.0. For grid functions, check out Bourbon's sister library" +
"Neat.";
}
$width: $columns * $fg-column + ($columns - 1) * $fg-gutter;
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
@return percentage($width / $container-width);
}
// Flexible gutter
@function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `flex-gutter` is deprecated and will be " +
"removed in 5.0.0. For grid functions, check out Bourbon's sister library" +
"Neat.";
}
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
@return percentage($gutter / $container-width);
}
@function grid-width($n) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `grid-width` is deprecated and will be " +
"removed in 5.0.0. For grid functions, check out Bourbon's sister library" +
"Neat.";
}
@return $n * $gw-column + ($n - 1) * $gw-gutter;
}
@function golden-ratio($value, $increment) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `golden-ratio` is deprecated and will be " +
"removed in 5.0.0. You can use the `modular-scale` function instead.";
}
@return modular-scale($increment, $value, $ratio: $golden);
}
@mixin box-sizing($box) {
@include _bourbon-deprecate-for-prefixing("box-sizing");
@include prefixer(box-sizing, $box, webkit moz spec);
}
+90
View File
@@ -0,0 +1,90 @@
// Bourbon 4.3.0
// http://bourbon.io
// Copyright 2011-2017 thoughtbot, inc.
// MIT License
@import "settings/deprecation-warnings";
@import "settings/prefixer";
@import "settings/px-to-em";
@import "settings/asset-pipeline";
@import "bourbon-deprecate";
@import "functions/assign-inputs";
@import "functions/contains";
@import "functions/contains-falsy";
@import "functions/is-length";
@import "functions/is-light";
@import "functions/is-number";
@import "functions/is-size";
@import "functions/px-to-em";
@import "functions/px-to-rem";
@import "functions/shade";
@import "functions/strip-units";
@import "functions/tint";
@import "functions/transition-property-name";
@import "functions/unpack";
@import "functions/modular-scale";
@import "helpers/convert-units";
@import "helpers/directional-values";
@import "helpers/font-source-declaration";
@import "helpers/gradient-positions-parser";
@import "helpers/linear-angle-parser";
@import "helpers/linear-gradient-parser";
@import "helpers/linear-positions-parser";
@import "helpers/linear-side-corner-parser";
@import "helpers/radial-arg-parser";
@import "helpers/radial-positions-parser";
@import "helpers/radial-gradient-parser";
@import "helpers/render-gradients";
@import "helpers/shape-size-stripper";
@import "helpers/str-to-num";
@import "css3/animation";
@import "css3/appearance";
@import "css3/backface-visibility";
@import "css3/background";
@import "css3/background-image";
@import "css3/border-image";
@import "css3/calc";
@import "css3/columns";
@import "css3/filter";
@import "css3/flex-box";
@import "css3/font-face";
@import "css3/font-feature-settings";
@import "css3/hidpi-media-query";
@import "css3/hyphens";
@import "css3/image-rendering";
@import "css3/keyframes";
@import "css3/linear-gradient";
@import "css3/perspective";
@import "css3/placeholder";
@import "css3/radial-gradient";
@import "css3/selection";
@import "css3/text-decoration";
@import "css3/transform";
@import "css3/transition";
@import "css3/user-select";
@import "addons/border-color";
@import "addons/border-radius";
@import "addons/border-style";
@import "addons/border-width";
@import "addons/buttons";
@import "addons/clearfix";
@import "addons/ellipsis";
@import "addons/font-stacks";
@import "addons/hide-text";
@import "addons/margin";
@import "addons/padding";
@import "addons/position";
@import "addons/prefixer";
@import "addons/retina-image";
@import "addons/size";
@import "addons/text-inputs";
@import "addons/timing-functions";
@import "addons/triangle";
@import "addons/word-wrap";
@import "bourbon-deprecated-upcoming";
+26
View File
@@ -0,0 +1,26 @@
@charset "UTF-8";
/// Provides a quick method for targeting `border-color` on specific sides of a box. Use a `null` value to “skip” a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include border-color(#a60b55 #76cd9c null #e8ae1a);
/// }
///
/// @example css - CSS Output
/// .element {
/// border-left-color: #e8ae1a;
/// border-right-color: #76cd9c;
/// border-top-color: #a60b55;
/// }
///
/// @require {mixin} directional-property
///
/// @output `border-color`
@mixin border-color($vals...) {
@include directional-property(border, color, $vals...);
}
+48
View File
@@ -0,0 +1,48 @@
@charset "UTF-8";
/// Provides a quick method for targeting `border-radius` on both corners on the side of a box.
///
/// @param {Number} $radii
/// List of arguments
///
/// @example scss - Usage
/// .element-one {
/// @include border-top-radius(5px);
/// }
///
/// .element-two {
/// @include border-left-radius(3px);
/// }
///
/// @example css - CSS Output
/// .element-one {
/// border-top-left-radius: 5px;
/// border-top-right-radius: 5px;
/// }
///
/// .element-two {
/// border-bottom-left-radius: 3px;
/// border-top-left-radius: 3px;
/// }
///
/// @output `border-radius`
@mixin border-top-radius($radii) {
border-top-left-radius: $radii;
border-top-right-radius: $radii;
}
@mixin border-right-radius($radii) {
border-bottom-right-radius: $radii;
border-top-right-radius: $radii;
}
@mixin border-bottom-radius($radii) {
border-bottom-left-radius: $radii;
border-bottom-right-radius: $radii;
}
@mixin border-left-radius($radii) {
border-bottom-left-radius: $radii;
border-top-left-radius: $radii;
}
+25
View File
@@ -0,0 +1,25 @@
@charset "UTF-8";
/// Provides a quick method for targeting `border-style` on specific sides of a box. Use a `null` value to “skip” a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include border-style(dashed null solid);
/// }
///
/// @example css - CSS Output
/// .element {
/// border-bottom-style: solid;
/// border-top-style: dashed;
/// }
///
/// @require {mixin} directional-property
///
/// @output `border-style`
@mixin border-style($vals...) {
@include directional-property(border, style, $vals...);
}
+25
View File
@@ -0,0 +1,25 @@
@charset "UTF-8";
/// Provides a quick method for targeting `border-width` on specific sides of a box. Use a `null` value to “skip” a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include border-width(1em null 20px);
/// }
///
/// @example css - CSS Output
/// .element {
/// border-bottom-width: 20px;
/// border-top-width: 1em;
/// }
///
/// @require {mixin} directional-property
///
/// @output `border-width`
@mixin border-width($vals...) {
@include directional-property(border, width, $vals...);
}
+64
View File
@@ -0,0 +1,64 @@
@charset "UTF-8";
/// Generates variables for all buttons. Please note that you must use interpolation on the variable: `#{$all-buttons}`.
///
/// @example scss - Usage
/// #{$all-buttons} {
/// background-color: #f00;
/// }
///
/// #{$all-buttons-focus},
/// #{$all-buttons-hover} {
/// background-color: #0f0;
/// }
///
/// #{$all-buttons-active} {
/// background-color: #00f;
/// }
///
/// @example css - CSS Output
/// button,
/// input[type="button"],
/// input[type="reset"],
/// input[type="submit"] {
/// background-color: #f00;
/// }
///
/// button:focus,
/// input[type="button"]:focus,
/// input[type="reset"]:focus,
/// input[type="submit"]:focus,
/// button:hover,
/// input[type="button"]:hover,
/// input[type="reset"]:hover,
/// input[type="submit"]:hover {
/// background-color: #0f0;
/// }
///
/// button:active,
/// input[type="button"]:active,
/// input[type="reset"]:active,
/// input[type="submit"]:active {
/// background-color: #00f;
/// }
///
/// @require assign-inputs
///
/// @type List
///
/// @todo Remove double assigned variables (Lines 5962) in v5.0.0
$buttons-list: 'button',
'input[type="button"]',
'input[type="reset"]',
'input[type="submit"]';
$all-buttons: assign-inputs($buttons-list);
$all-buttons-active: assign-inputs($buttons-list, active);
$all-buttons-focus: assign-inputs($buttons-list, focus);
$all-buttons-hover: assign-inputs($buttons-list, hover);
$all-button-inputs: $all-buttons;
$all-button-inputs-active: $all-buttons-active;
$all-button-inputs-focus: $all-buttons-focus;
$all-button-inputs-hover: $all-buttons-hover;
+25
View File
@@ -0,0 +1,25 @@
@charset "UTF-8";
/// Provides an easy way to include a clearfix for containing floats.
///
/// @link http://cssmojo.com/latest_new_clearfix_so_far/
///
/// @example scss - Usage
/// .element {
/// @include clearfix;
/// }
///
/// @example css - CSS Output
/// .element::after {
/// clear: both;
/// content: "";
/// display: table;
/// }
@mixin clearfix {
&::after {
clear: both;
content: "";
display: table;
}
}
+30
View File
@@ -0,0 +1,30 @@
@charset "UTF-8";
/// Truncates text and adds an ellipsis to represent overflow.
///
/// @param {Number} $width [100%]
/// Max-width for the string to respect before being truncated
///
/// @example scss - Usage
/// .element {
/// @include ellipsis;
/// }
///
/// @example css - CSS Output
/// .element {
/// display: inline-block;
/// max-width: 100%;
/// overflow: hidden;
/// text-overflow: ellipsis;
/// white-space: nowrap;
/// word-wrap: normal;
/// }
@mixin ellipsis($width: 100%) {
display: inline-block;
max-width: $width;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
}
+31
View File
@@ -0,0 +1,31 @@
@charset "UTF-8";
/// Georgia font stack.
///
/// @type List
$georgia: "Georgia", "Cambria", "Times New Roman", "Times", serif;
/// Helvetica font stack.
///
/// @type List
$helvetica: "Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif;
/// Lucida Grande font stack.
///
/// @type List
$lucida-grande: "Lucida Grande", "Tahoma", "Verdana", "Arial", sans-serif;
/// Monospace font stack.
///
/// @type List
$monospace: "Bitstream Vera Sans Mono", "Consolas", "Courier", monospace;
/// Verdana font stack.
///
/// @type List
$verdana: "Verdana", "Geneva", sans-serif;
+27
View File
@@ -0,0 +1,27 @@
/// Hides the text in an element, commonly used to show an image. Some elements will need block-level styles applied.
///
/// @link http://zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement
///
/// @example scss - Usage
/// .element {
/// @include hide-text;
/// }
///
/// @example css - CSS Output
/// .element {
/// overflow: hidden;
/// text-indent: 101%;
/// white-space: nowrap;
/// }
///
/// @todo Remove height argument in v5.0.0
@mixin hide-text($height: null) {
overflow: hidden;
text-indent: 101%;
white-space: nowrap;
@if $height {
@warn "The `hide-text` mixin has changed and no longer requires a height. The height argument will no longer be accepted in v5.0.0";
}
}
+26
View File
@@ -0,0 +1,26 @@
@charset "UTF-8";
/// Provides a quick method for targeting `margin` on specific sides of a box. Use a `null` value to “skip” a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include margin(null 10px 3em 20vh);
/// }
///
/// @example css - CSS Output
/// .element {
/// margin-bottom: 3em;
/// margin-left: 20vh;
/// margin-right: 10px;
/// }
///
/// @require {mixin} directional-property
///
/// @output `margin`
@mixin margin($vals...) {
@include directional-property(margin, false, $vals...);
}
+26
View File
@@ -0,0 +1,26 @@
@charset "UTF-8";
/// Provides a quick method for targeting `padding` on specific sides of a box. Use a `null` value to “skip” a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include padding(12vh null 10px 5%);
/// }
///
/// @example css - CSS Output
/// .element {
/// padding-bottom: 10px;
/// padding-left: 5%;
/// padding-top: 12vh;
/// }
///
/// @require {mixin} directional-property
///
/// @output `padding`
@mixin padding($vals...) {
@include directional-property(padding, false, $vals...);
}
+48
View File
@@ -0,0 +1,48 @@
@charset "UTF-8";
/// Provides a quick method for setting an elements position. Use a `null` value to “skip” a side.
///
/// @param {Position} $position [relative]
/// A CSS position value
///
/// @param {Arglist} $coordinates [null null null null]
/// List of values that correspond to the 4-value syntax for the edges of a box
///
/// @example scss - Usage
/// .element {
/// @include position(absolute, 0 null null 10em);
/// }
///
/// @example css - CSS Output
/// .element {
/// left: 10em;
/// position: absolute;
/// top: 0;
/// }
///
/// @require {function} is-length
/// @require {function} unpack
@mixin position($position: relative, $coordinates: null null null null) {
@if type-of($position) == list {
$coordinates: $position;
$position: relative;
}
$coordinates: unpack($coordinates);
$offsets: (
top: nth($coordinates, 1),
right: nth($coordinates, 2),
bottom: nth($coordinates, 3),
left: nth($coordinates, 4)
);
position: $position;
@each $offset, $value in $offsets {
@if is-length($value) {
#{$offset}: $value;
}
}
}
+66
View File
@@ -0,0 +1,66 @@
@charset "UTF-8";
/// A mixin for generating vendor prefixes on non-standardized properties.
///
/// @param {String} $property
/// Property to prefix
///
/// @param {*} $value
/// Value to use
///
/// @param {List} $prefixes
/// Prefixes to define
///
/// @example scss - Usage
/// .element {
/// @include prefixer(border-radius, 10px, webkit ms spec);
/// }
///
/// @example css - CSS Output
/// .element {
/// -webkit-border-radius: 10px;
/// -moz-border-radius: 10px;
/// border-radius: 10px;
/// }
///
/// @require {variable} $prefix-for-webkit
/// @require {variable} $prefix-for-mozilla
/// @require {variable} $prefix-for-microsoft
/// @require {variable} $prefix-for-opera
/// @require {variable} $prefix-for-spec
@mixin prefixer($property, $value, $prefixes) {
@each $prefix in $prefixes {
@if $prefix == webkit {
@if $prefix-for-webkit {
-webkit-#{$property}: $value;
}
} @else if $prefix == moz {
@if $prefix-for-mozilla {
-moz-#{$property}: $value;
}
} @else if $prefix == ms {
@if $prefix-for-microsoft {
-ms-#{$property}: $value;
}
} @else if $prefix == o {
@if $prefix-for-opera {
-o-#{$property}: $value;
}
} @else if $prefix == spec {
@if $prefix-for-spec {
#{$property}: $value;
}
} @else {
@warn "Unrecognized prefix: #{$prefix}";
}
}
}
@mixin disable-prefix-for-all() {
$prefix-for-webkit: false !global;
$prefix-for-mozilla: false !global;
$prefix-for-microsoft: false !global;
$prefix-for-opera: false !global;
$prefix-for-spec: false !global;
}
+27
View File
@@ -0,0 +1,27 @@
@mixin retina-image($filename, $background-size, $extension: png, $retina-filename: null, $retina-suffix: _2x, $asset-pipeline: $asset-pipeline) {
@include _bourbon-deprecate("retina-image");
@if $asset-pipeline {
background-image: image-url("#{$filename}.#{$extension}");
} @else {
background-image: url("#{$filename}.#{$extension}");
}
@include hidpi {
@if $asset-pipeline {
@if $retina-filename {
background-image: image-url("#{$retina-filename}.#{$extension}");
} @else {
background-image: image-url("#{$filename}#{$retina-suffix}.#{$extension}");
}
} @else {
@if $retina-filename {
background-image: url("#{$retina-filename}.#{$extension}");
} @else {
background-image: url("#{$filename}#{$retina-suffix}.#{$extension}");
}
}
background-size: $background-size;
}
}
+51
View File
@@ -0,0 +1,51 @@
@charset "UTF-8";
/// Sets the `width` and `height` of the element.
///
/// @param {List} $size
/// A list of at most 2 size values.
///
/// If there is only a single value in `$size` it is used for both width and height. All units are supported.
///
/// @example scss - Usage
/// .first-element {
/// @include size(2em);
/// }
///
/// .second-element {
/// @include size(auto 10em);
/// }
///
/// @example css - CSS Output
/// .first-element {
/// width: 2em;
/// height: 2em;
/// }
///
/// .second-element {
/// width: auto;
/// height: 10em;
/// }
///
/// @todo Refactor in 5.0.0 to use a comma-separated argument
@mixin size($value) {
$width: nth($value, 1);
$height: $width;
@if length($value) > 1 {
$height: nth($value, 2);
}
@if is-size($height) {
height: $height;
} @else {
@warn "`#{$height}` is not a valid length for the `$height` parameter in the `size` mixin.";
}
@if is-size($width) {
width: $width;
} @else {
@warn "`#{$width}` is not a valid length for the `$width` parameter in the `size` mixin.";
}
}
+113
View File
@@ -0,0 +1,113 @@
@charset "UTF-8";
/// Generates variables for all text-based inputs. Please note that you must use interpolation on the variable: `#{$all-text-inputs}`.
///
/// @example scss - Usage
/// #{$all-text-inputs} {
/// border: 1px solid #f00;
/// }
///
/// #{$all-text-inputs-focus},
/// #{$all-text-inputs-hover} {
/// border: 1px solid #0f0;
/// }
///
/// #{$all-text-inputs-active} {
/// border: 1px solid #00f;
/// }
///
/// @example css - CSS Output
/// input[type="color"],
/// input[type="date"],
/// input[type="datetime"],
/// input[type="datetime-local"],
/// input[type="email"],
/// input[type="month"],
/// input[type="number"],
/// input[type="password"],
/// input[type="search"],
/// input[type="tel"],
/// input[type="text"],
/// input[type="time"],
/// input[type="url"],
/// input[type="week"],
/// textarea {
/// border: 1px solid #f00;
/// }
///
/// input[type="color"]:focus,
/// input[type="date"]:focus,
/// input[type="datetime"]:focus,
/// input[type="datetime-local"]:focus,
/// input[type="email"]:focus,
/// input[type="month"]:focus,
/// input[type="number"]:focus,
/// input[type="password"]:focus,
/// input[type="search"]:focus,
/// input[type="tel"]:focus,
/// input[type="text"]:focus,
/// input[type="time"]:focus,
/// input[type="url"]:focus,
/// input[type="week"]:focus,
/// textarea:focus,
/// input[type="color"]:hover,
/// input[type="date"]:hover,
/// input[type="datetime"]:hover,
/// input[type="datetime-local"]:hover,
/// input[type="email"]:hover,
/// input[type="month"]:hover,
/// input[type="number"]:hover,
/// input[type="password"]:hover,
/// input[type="search"]:hover,
/// input[type="tel"]:hover,
/// input[type="text"]:hover,
/// input[type="time"]:hover,
/// input[type="url"]:hover,
/// input[type="week"]:hover,
/// textarea:hover {
/// border: 1px solid #0f0;
/// }
///
/// input[type="color"]:active,
/// input[type="date"]:active,
/// input[type="datetime"]:active,
/// input[type="datetime-local"]:active,
/// input[type="email"]:active,
/// input[type="month"]:active,
/// input[type="number"]:active,
/// input[type="password"]:active,
/// input[type="search"]:active,
/// input[type="tel"]:active,
/// input[type="text"]:active,
/// input[type="time"]:active,
/// input[type="url"]:active,
/// input[type="week"]:active,
/// textarea:active {
/// border: 1px solid #00f;
/// }
///
/// @require assign-inputs
///
/// @type List
$text-inputs-list: 'input[type="color"]',
'input[type="date"]',
'input[type="datetime"]',
'input[type="datetime-local"]',
'input[type="email"]',
'input[type="month"]',
'input[type="number"]',
'input[type="password"]',
'input[type="search"]',
'input[type="tel"]',
'input[type="text"]',
'input[type="time"]',
'input[type="url"]',
'input[type="week"]',
'input:not([type])',
'textarea';
$all-text-inputs: assign-inputs($text-inputs-list);
$all-text-inputs-active: assign-inputs($text-inputs-list, active);
$all-text-inputs-focus: assign-inputs($text-inputs-list, focus);
$all-text-inputs-hover: assign-inputs($text-inputs-list, hover);
+34
View File
@@ -0,0 +1,34 @@
@charset "UTF-8";
/// CSS cubic-bezier timing functions. Timing functions courtesy of jquery.easie (github.com/jaukia/easie)
///
/// Timing functions are the same as demoed here: http://jqueryui.com/resources/demos/effect/easing.html
///
/// @type cubic-bezier
$ease-in-quad: cubic-bezier(0.550, 0.085, 0.680, 0.530);
$ease-in-cubic: cubic-bezier(0.550, 0.055, 0.675, 0.190);
$ease-in-quart: cubic-bezier(0.895, 0.030, 0.685, 0.220);
$ease-in-quint: cubic-bezier(0.755, 0.050, 0.855, 0.060);
$ease-in-sine: cubic-bezier(0.470, 0.000, 0.745, 0.715);
$ease-in-expo: cubic-bezier(0.950, 0.050, 0.795, 0.035);
$ease-in-circ: cubic-bezier(0.600, 0.040, 0.980, 0.335);
$ease-in-back: cubic-bezier(0.600, -0.280, 0.735, 0.045);
$ease-out-quad: cubic-bezier(0.250, 0.460, 0.450, 0.940);
$ease-out-cubic: cubic-bezier(0.215, 0.610, 0.355, 1.000);
$ease-out-quart: cubic-bezier(0.165, 0.840, 0.440, 1.000);
$ease-out-quint: cubic-bezier(0.230, 1.000, 0.320, 1.000);
$ease-out-sine: cubic-bezier(0.390, 0.575, 0.565, 1.000);
$ease-out-expo: cubic-bezier(0.190, 1.000, 0.220, 1.000);
$ease-out-circ: cubic-bezier(0.075, 0.820, 0.165, 1.000);
$ease-out-back: cubic-bezier(0.175, 0.885, 0.320, 1.275);
$ease-in-out-quad: cubic-bezier(0.455, 0.030, 0.515, 0.955);
$ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1.000);
$ease-in-out-quart: cubic-bezier(0.770, 0.000, 0.175, 1.000);
$ease-in-out-quint: cubic-bezier(0.860, 0.000, 0.070, 1.000);
$ease-in-out-sine: cubic-bezier(0.445, 0.050, 0.550, 0.950);
$ease-in-out-expo: cubic-bezier(1.000, 0.000, 0.000, 1.000);
$ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.150, 0.860);
$ease-in-out-back: cubic-bezier(0.680, -0.550, 0.265, 1.550);
+63
View File
@@ -0,0 +1,63 @@
@mixin triangle($size, $color, $direction) {
$width: nth($size, 1);
$height: nth($size, length($size));
$foreground-color: nth($color, 1);
$background-color: if(length($color) == 2, nth($color, 2), transparent);
height: 0;
width: 0;
@if ($direction == up) or ($direction == down) or ($direction == right) or ($direction == left) {
$width: $width / 2;
$height: if(length($size) > 1, $height, $height/2);
@if $direction == up {
border-bottom: $height solid $foreground-color;
border-left: $width solid $background-color;
border-right: $width solid $background-color;
} @else if $direction == right {
border-bottom: $width solid $background-color;
border-left: $height solid $foreground-color;
border-top: $width solid $background-color;
} @else if $direction == down {
border-left: $width solid $background-color;
border-right: $width solid $background-color;
border-top: $height solid $foreground-color;
} @else if $direction == left {
border-bottom: $width solid $background-color;
border-right: $height solid $foreground-color;
border-top: $width solid $background-color;
}
} @else if ($direction == up-right) or ($direction == up-left) {
border-top: $height solid $foreground-color;
@if $direction == up-right {
border-left: $width solid $background-color;
} @else if $direction == up-left {
border-right: $width solid $background-color;
}
} @else if ($direction == down-right) or ($direction == down-left) {
border-bottom: $height solid $foreground-color;
@if $direction == down-right {
border-left: $width solid $background-color;
} @else if $direction == down-left {
border-right: $width solid $background-color;
}
} @else if ($direction == inset-up) {
border-color: $background-color $background-color $foreground-color;
border-style: solid;
border-width: $height $width;
} @else if ($direction == inset-down) {
border-color: $foreground-color $background-color $background-color;
border-style: solid;
border-width: $height $width;
} @else if ($direction == inset-right) {
border-color: $background-color $background-color $background-color $foreground-color;
border-style: solid;
border-width: $width $height;
} @else if ($direction == inset-left) {
border-color: $background-color $foreground-color $background-color $background-color;
border-style: solid;
border-width: $width $height;
}
}
+29
View File
@@ -0,0 +1,29 @@
@charset "UTF-8";
/// Provides an easy way to change the `word-wrap` property.
///
/// @param {String} $wrap [break-word]
/// Value for the `word-break` property.
///
/// @example scss - Usage
/// .wrapper {
/// @include word-wrap(break-word);
/// }
///
/// @example css - CSS Output
/// .wrapper {
/// overflow-wrap: break-word;
/// word-break: break-all;
/// word-wrap: break-word;
/// }
@mixin word-wrap($wrap: break-word) {
overflow-wrap: $wrap;
word-wrap: $wrap;
@if $wrap == break-word {
word-break: break-all;
} @else {
word-break: $wrap;
}
}
+61
View File
@@ -0,0 +1,61 @@
// http://www.w3.org/TR/css3-animations/#the-animation-name-property-
// Each of these mixins support comma separated lists of values, which allows different transitions for individual properties to be described in a single style rule. Each value in the list corresponds to the value at that same position in the other properties.
@mixin animation($animations...) {
@include _bourbon-deprecate-for-prefixing("animation");
@include prefixer(animation, $animations, webkit moz spec);
}
@mixin animation-name($names...) {
@include _bourbon-deprecate-for-prefixing("animation-name");
@include prefixer(animation-name, $names, webkit moz spec);
}
@mixin animation-duration($times...) {
@include _bourbon-deprecate-for-prefixing("animation-duration");
@include prefixer(animation-duration, $times, webkit moz spec);
}
@mixin animation-timing-function($motions...) {
@include _bourbon-deprecate-for-prefixing("animation-timing-function");
// ease | linear | ease-in | ease-out | ease-in-out
@include prefixer(animation-timing-function, $motions, webkit moz spec);
}
@mixin animation-iteration-count($values...) {
@include _bourbon-deprecate-for-prefixing("animation-iteration-count");
// infinite | <number>
@include prefixer(animation-iteration-count, $values, webkit moz spec);
}
@mixin animation-direction($directions...) {
@include _bourbon-deprecate-for-prefixing("animation-direction");
// normal | alternate
@include prefixer(animation-direction, $directions, webkit moz spec);
}
@mixin animation-play-state($states...) {
@include _bourbon-deprecate-for-prefixing("animation-play-state");
// running | paused
@include prefixer(animation-play-state, $states, webkit moz spec);
}
@mixin animation-delay($times...) {
@include _bourbon-deprecate-for-prefixing("animation-delay");
@include prefixer(animation-delay, $times, webkit moz spec);
}
@mixin animation-fill-mode($modes...) {
@include _bourbon-deprecate-for-prefixing("animation-fill-mode");
// none | forwards | backwards | both
@include prefixer(animation-fill-mode, $modes, webkit moz spec);
}
+5
View File
@@ -0,0 +1,5 @@
@mixin appearance($value) {
@include _bourbon-deprecate-for-prefixing("appearance");
@include prefixer(appearance, $value, webkit moz ms o spec);
}
+5
View File
@@ -0,0 +1,5 @@
@mixin backface-visibility($visibility) {
@include _bourbon-deprecate-for-prefixing("backface-visibility");
@include prefixer(backface-visibility, $visibility, webkit spec);
}
+44
View File
@@ -0,0 +1,44 @@
//************************************************************************//
// Background-image property for adding multiple background images with
// gradients, or for stringing multiple gradients together.
//************************************************************************//
@mixin background-image($images...) {
@include _bourbon-deprecate-for-prefixing("background-image");
$webkit-images: ();
$spec-images: ();
@each $image in $images {
$webkit-image: ();
$spec-image: ();
@if (type-of($image) == string) {
$url-str: str-slice($image, 1, 3);
$gradient-type: str-slice($image, 1, 6);
@if $url-str == "url" {
$webkit-image: $image;
$spec-image: $image;
}
@else if $gradient-type == "linear" {
$gradients: _linear-gradient-parser($image);
$webkit-image: map-get($gradients, webkit-image);
$spec-image: map-get($gradients, spec-image);
}
@else if $gradient-type == "radial" {
$gradients: _radial-gradient-parser($image);
$webkit-image: map-get($gradients, webkit-image);
$spec-image: map-get($gradients, spec-image);
}
}
$webkit-images: append($webkit-images, $webkit-image, comma);
$spec-images: append($spec-images, $spec-image, comma);
}
background-image: $webkit-images;
background-image: $spec-images;
}
+57
View File
@@ -0,0 +1,57 @@
//************************************************************************//
// Background property for adding multiple backgrounds using shorthand
// notation.
//************************************************************************//
@mixin background($backgrounds...) {
@include _bourbon-deprecate-for-prefixing("background");
$webkit-backgrounds: ();
$spec-backgrounds: ();
@each $background in $backgrounds {
$webkit-background: ();
$spec-background: ();
$background-type: type-of($background);
@if $background-type == string or $background-type == list {
$background-str: if($background-type == list, nth($background, 1), $background);
$url-str: str-slice($background-str, 1, 3);
$gradient-type: str-slice($background-str, 1, 6);
@if $url-str == "url" {
$webkit-background: $background;
$spec-background: $background;
}
@else if $gradient-type == "linear" {
$gradients: _linear-gradient-parser("#{$background}");
$webkit-background: map-get($gradients, webkit-image);
$spec-background: map-get($gradients, spec-image);
}
@else if $gradient-type == "radial" {
$gradients: _radial-gradient-parser("#{$background}");
$webkit-background: map-get($gradients, webkit-image);
$spec-background: map-get($gradients, spec-image);
}
@else {
$webkit-background: $background;
$spec-background: $background;
}
}
@else {
$webkit-background: $background;
$spec-background: $background;
}
$webkit-backgrounds: append($webkit-backgrounds, $webkit-background, comma);
$spec-backgrounds: append($spec-backgrounds, $spec-background, comma);
}
background: $webkit-backgrounds;
background: $spec-backgrounds;
}
+61
View File
@@ -0,0 +1,61 @@
@mixin border-image($borders...) {
@include _bourbon-deprecate-for-prefixing("border-image");
$webkit-borders: ();
$spec-borders: ();
@each $border in $borders {
$webkit-border: ();
$spec-border: ();
$border-type: type-of($border);
@if $border-type == string or list {
$border-str: if($border-type == list, nth($border, 1), $border);
$url-str: str-slice($border-str, 1, 3);
$gradient-type: str-slice($border-str, 1, 6);
@if $url-str == "url" {
$webkit-border: $border;
$spec-border: $border;
}
@else if $gradient-type == "linear" {
$gradients: _linear-gradient-parser("#{$border}");
$webkit-border: map-get($gradients, webkit-image);
$spec-border: map-get($gradients, spec-image);
}
@else if $gradient-type == "radial" {
$gradients: _radial-gradient-parser("#{$border}");
$webkit-border: map-get($gradients, webkit-image);
$spec-border: map-get($gradients, spec-image);
}
@else {
$webkit-border: $border;
$spec-border: $border;
}
}
@else {
$webkit-border: $border;
$spec-border: $border;
}
$webkit-borders: append($webkit-borders, $webkit-border, comma);
$spec-borders: append($spec-borders, $spec-border, comma);
}
-webkit-border-image: $webkit-borders;
border-image: $spec-borders;
border-style: solid;
}
//Examples:
// @include border-image(url("image.png"));
// @include border-image(url("image.png") 20 stretch);
// @include border-image(linear-gradient(45deg, orange, yellow));
// @include border-image(linear-gradient(45deg, orange, yellow) stretch);
// @include border-image(linear-gradient(45deg, orange, yellow) 20 30 40 50 stretch round);
// @include border-image(radial-gradient(top, cover, orange, yellow, orange));
+6
View File
@@ -0,0 +1,6 @@
@mixin calc($property, $value) {
@include _bourbon-deprecate-for-prefixing("calc");
#{$property}: -webkit-calc(#{$value});
#{$property}: calc(#{$value});
}
+67
View File
@@ -0,0 +1,67 @@
@mixin columns($arg: auto) {
@include _bourbon-deprecate-for-prefixing("columns");
// <column-count> || <column-width>
@include prefixer(columns, $arg, webkit moz spec);
}
@mixin column-count($int: auto) {
@include _bourbon-deprecate-for-prefixing("column-count");
// auto || integer
@include prefixer(column-count, $int, webkit moz spec);
}
@mixin column-gap($length: normal) {
@include _bourbon-deprecate-for-prefixing("column-gap");
// normal || length
@include prefixer(column-gap, $length, webkit moz spec);
}
@mixin column-fill($arg: auto) {
@include _bourbon-deprecate-for-prefixing("column-fill");
// auto || length
@include prefixer(column-fill, $arg, webkit moz spec);
}
@mixin column-rule($arg) {
@include _bourbon-deprecate-for-prefixing("column-rule");
// <border-width> || <border-style> || <color>
@include prefixer(column-rule, $arg, webkit moz spec);
}
@mixin column-rule-color($color) {
@include _bourbon-deprecate-for-prefixing("column-rule-color");
@include prefixer(column-rule-color, $color, webkit moz spec);
}
@mixin column-rule-style($style: none) {
@include _bourbon-deprecate-for-prefixing("column-rule-style");
// none | hidden | dashed | dotted | double | groove | inset | inset | outset | ridge | solid
@include prefixer(column-rule-style, $style, webkit moz spec);
}
@mixin column-rule-width($width: none) {
@include _bourbon-deprecate-for-prefixing("column-rule-width");
@include prefixer(column-rule-width, $width, webkit moz spec);
}
@mixin column-span($arg: none) {
@include _bourbon-deprecate-for-prefixing("column-span");
// none || all
@include prefixer(column-span, $arg, webkit moz spec);
}
@mixin column-width($length: auto) {
@include _bourbon-deprecate-for-prefixing("column-width");
// auto || length
@include prefixer(column-width, $length, webkit moz spec);
}
+6
View File
@@ -0,0 +1,6 @@
@mixin filter($function: none) {
@include _bourbon-deprecate-for-prefixing("filter");
// <filter-function> [<filter-function]* | none
@include prefixer(filter, $function, webkit spec);
}
+327
View File
@@ -0,0 +1,327 @@
// CSS3 Flexible Box Model and property defaults
// Custom shorthand notation for flexbox
@mixin box($orient: inline-axis, $pack: start, $align: stretch) {
@include _bourbon-deprecate-for-prefixing("box");
@include display-box;
@include box-orient($orient);
@include box-pack($pack);
@include box-align($align);
}
@mixin display-box {
@include _bourbon-deprecate-for-prefixing("display-box");
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox; // IE 10
display: box;
}
@mixin box-orient($orient: inline-axis) {
@include _bourbon-deprecate-for-prefixing("box-orient");
// horizontal|vertical|inline-axis|block-axis|inherit
@include prefixer(box-orient, $orient, webkit moz spec);
}
@mixin box-pack($pack: start) {
@include _bourbon-deprecate-for-prefixing("box-pack");
// start|end|center|justify
@include prefixer(box-pack, $pack, webkit moz spec);
-ms-flex-pack: $pack; // IE 10
}
@mixin box-align($align: stretch) {
@include _bourbon-deprecate-for-prefixing("box-align");
// start|end|center|baseline|stretch
@include prefixer(box-align, $align, webkit moz spec);
-ms-flex-align: $align; // IE 10
}
@mixin box-direction($direction: normal) {
@include _bourbon-deprecate-for-prefixing("box-direction");
// normal|reverse|inherit
@include prefixer(box-direction, $direction, webkit moz spec);
-ms-flex-direction: $direction; // IE 10
}
@mixin box-lines($lines: single) {
@include _bourbon-deprecate-for-prefixing("box-lines");
// single|multiple
@include prefixer(box-lines, $lines, webkit moz spec);
}
@mixin box-ordinal-group($int: 1) {
@include _bourbon-deprecate-for-prefixing("box-ordinal-group");
@include prefixer(box-ordinal-group, $int, webkit moz spec);
-ms-flex-order: $int; // IE 10
}
@mixin box-flex($value: 0) {
@include _bourbon-deprecate-for-prefixing("box-flex");
@include prefixer(box-flex, $value, webkit moz spec);
-ms-flex: $value; // IE 10
}
@mixin box-flex-group($int: 1) {
@include _bourbon-deprecate-for-prefixing("box-flex-group");
@include prefixer(box-flex-group, $int, webkit moz spec);
}
// CSS3 Flexible Box Model and property defaults
// Unified attributes for 2009, 2011, and 2012 flavours.
// 2009 - display (box | inline-box)
// 2011 - display (flexbox | inline-flexbox)
// 2012 - display (flex | inline-flex)
@mixin display($value) {
@include _bourbon-deprecate-for-prefixing("display");
// flex | inline-flex
@if $value == "flex" {
// 2009
display: -webkit-box;
display: -moz-box;
display: box;
// 2012
display: -webkit-flex;
display: -moz-flex;
display: -ms-flexbox; // 2011 (IE 10)
display: flex;
} @else if $value == "inline-flex" {
display: -webkit-inline-box;
display: -moz-inline-box;
display: inline-box;
display: -webkit-inline-flex;
display: -moz-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
} @else {
display: $value;
}
}
// 2009 - box-flex (integer)
// 2011 - flex (decimal | width decimal)
// 2012 - flex (integer integer width)
@mixin flex($value) {
@include _bourbon-deprecate-for-prefixing("flex");
// Grab flex-grow for older browsers.
$flex-grow: nth($value, 1);
// 2009
@include prefixer(box-flex, $flex-grow, webkit moz spec);
// 2011 (IE 10), 2012
@include prefixer(flex, $value, webkit moz ms spec);
}
// 2009 - box-orient ( horizontal | vertical | inline-axis | block-axis)
// - box-direction (normal | reverse)
// 2011 - flex-direction (row | row-reverse | column | column-reverse)
// 2012 - flex-direction (row | row-reverse | column | column-reverse)
@mixin flex-direction($value: row) {
@include _bourbon-deprecate-for-prefixing("flex-direction");
// Alt values.
$value-2009: $value;
$value-2011: $value;
$direction: normal;
@if $value == row {
$value-2009: horizontal;
} @else if $value == "row-reverse" {
$value-2009: horizontal;
$direction: reverse;
} @else if $value == column {
$value-2009: vertical;
} @else if $value == "column-reverse" {
$value-2009: vertical;
$direction: reverse;
}
// 2009
@include prefixer(box-orient, $value-2009, webkit moz spec);
@include prefixer(box-direction, $direction, webkit moz spec);
// 2012
@include prefixer(flex-direction, $value, webkit moz spec);
// 2011 (IE 10)
-ms-flex-direction: $value;
}
// 2009 - box-lines (single | multiple)
// 2011 - flex-wrap (nowrap | wrap | wrap-reverse)
// 2012 - flex-wrap (nowrap | wrap | wrap-reverse)
@mixin flex-wrap($value: nowrap) {
@include _bourbon-deprecate-for-prefixing("flex-wrap");
// Alt values
$alt-value: $value;
@if $value == nowrap {
$alt-value: single;
} @else if $value == wrap {
$alt-value: multiple;
} @else if $value == "wrap-reverse" {
$alt-value: multiple;
}
@include prefixer(box-lines, $alt-value, webkit moz spec);
@include prefixer(flex-wrap, $value, webkit moz ms spec);
}
// 2009 - TODO: parse values into flex-direction/flex-wrap
// 2011 - TODO: parse values into flex-direction/flex-wrap
// 2012 - flex-flow (flex-direction || flex-wrap)
@mixin flex-flow($value) {
@include _bourbon-deprecate-for-prefixing("flex-flow");
@include prefixer(flex-flow, $value, webkit moz spec);
}
// 2009 - box-ordinal-group (integer)
// 2011 - flex-order (integer)
// 2012 - order (integer)
@mixin order($int: 0) {
@include _bourbon-deprecate-for-prefixing("order");
// 2009
@include prefixer(box-ordinal-group, $int, webkit moz spec);
// 2012
@include prefixer(order, $int, webkit moz spec);
// 2011 (IE 10)
-ms-flex-order: $int;
}
// 2012 - flex-grow (number)
@mixin flex-grow($number: 0) {
@include _bourbon-deprecate-for-prefixing("flex-grow");
@include prefixer(flex-grow, $number, webkit moz spec);
-ms-flex-positive: $number;
}
// 2012 - flex-shrink (number)
@mixin flex-shrink($number: 1) {
@include _bourbon-deprecate-for-prefixing("flex-shrink");
@include prefixer(flex-shrink, $number, webkit moz spec);
-ms-flex-negative: $number;
}
// 2012 - flex-basis (number)
@mixin flex-basis($width: auto) {
@include _bourbon-deprecate-for-prefixing("flex-basis");
@include prefixer(flex-basis, $width, webkit moz spec);
-ms-flex-preferred-size: $width;
}
// 2009 - box-pack (start | end | center | justify)
// 2011 - flex-pack (start | end | center | justify)
// 2012 - justify-content (flex-start | flex-end | center | space-between | space-around)
@mixin justify-content($value: flex-start) {
@include _bourbon-deprecate-for-prefixing("justify-content");
// Alt values.
$alt-value: $value;
@if $value == "flex-start" {
$alt-value: start;
} @else if $value == "flex-end" {
$alt-value: end;
} @else if $value == "space-between" {
$alt-value: justify;
} @else if $value == "space-around" {
$alt-value: distribute;
}
// 2009
@include prefixer(box-pack, $alt-value, webkit moz spec);
// 2012
@include prefixer(justify-content, $value, webkit moz ms o spec);
// 2011 (IE 10)
-ms-flex-pack: $alt-value;
}
// 2009 - box-align (start | end | center | baseline | stretch)
// 2011 - flex-align (start | end | center | baseline | stretch)
// 2012 - align-items (flex-start | flex-end | center | baseline | stretch)
@mixin align-items($value: stretch) {
@include _bourbon-deprecate-for-prefixing("align-items");
$alt-value: $value;
@if $value == "flex-start" {
$alt-value: start;
} @else if $value == "flex-end" {
$alt-value: end;
}
// 2009
@include prefixer(box-align, $alt-value, webkit moz spec);
// 2012
@include prefixer(align-items, $value, webkit moz ms o spec);
// 2011 (IE 10)
-ms-flex-align: $alt-value;
}
// 2011 - flex-item-align (auto | start | end | center | baseline | stretch)
// 2012 - align-self (auto | flex-start | flex-end | center | baseline | stretch)
@mixin align-self($value: auto) {
@include _bourbon-deprecate-for-prefixing("align-self");
$value-2011: $value;
@if $value == "flex-start" {
$value-2011: start;
} @else if $value == "flex-end" {
$value-2011: end;
}
// 2012
@include prefixer(align-self, $value, webkit moz spec);
// 2011 (IE 10)
-ms-flex-item-align: $value-2011;
}
// 2011 - flex-line-pack (start | end | center | justify | distribute | stretch)
// 2012 - align-content (flex-start | flex-end | center | space-between | space-around | stretch)
@mixin align-content($value: stretch) {
@include _bourbon-deprecate-for-prefixing("align-content");
$value-2011: $value;
@if $value == "flex-start" {
$value-2011: start;
} @else if $value == "flex-end" {
$value-2011: end;
} @else if $value == "space-between" {
$value-2011: justify;
} @else if $value == "space-around" {
$value-2011: distribute;
}
// 2012
@include prefixer(align-content, $value, webkit moz spec);
// 2011 (IE 10)
-ms-flex-line-pack: $value-2011;
}
+24
View File
@@ -0,0 +1,24 @@
@mixin font-face(
$font-family,
$file-path,
$weight: normal,
$style: normal,
$asset-pipeline: $asset-pipeline,
$file-formats: eot woff2 woff ttf svg) {
$font-url-prefix: font-url-prefixer($asset-pipeline);
@font-face {
font-family: $font-family;
font-style: $style;
font-weight: $weight;
src: font-source-declaration(
$font-family,
$file-path,
$asset-pipeline,
$file-formats,
$font-url-prefix
);
}
}
+6
View File
@@ -0,0 +1,6 @@
@mixin font-feature-settings($settings...) {
@include _bourbon-deprecate-for-prefixing("font-feature-settings");
@if length($settings) == 0 { $settings: none; }
@include prefixer(font-feature-settings, $settings, webkit moz ms spec);
}
+12
View File
@@ -0,0 +1,12 @@
// HiDPI mixin. Default value set to 1.3 to target Google Nexus 7 (http://bjango.com/articles/min-device-pixel-ratio/)
@mixin hidpi($ratio: 1.3) {
@include _bourbon-deprecate-for-prefixing("hidpi");
@media only screen and (-webkit-min-device-pixel-ratio: $ratio),
only screen and (min--moz-device-pixel-ratio: $ratio),
only screen and (-o-min-device-pixel-ratio: #{$ratio}/1),
only screen and (min-resolution: round($ratio * 96dpi)),
only screen and (min-resolution: $ratio * 1dppx) {
@content;
}
}
+6
View File
@@ -0,0 +1,6 @@
@mixin hyphens($hyphenation: none) {
@include _bourbon-deprecate-for-prefixing("hyphens");
// none | manual | auto
@include prefixer(hyphens, $hyphenation, webkit moz ms spec);
}
+15
View File
@@ -0,0 +1,15 @@
@mixin image-rendering ($mode:auto) {
@include _bourbon-deprecate-for-prefixing("image-rendering");
@if ($mode == crisp-edges) {
-ms-interpolation-mode: nearest-neighbor; // IE8+
image-rendering: -moz-crisp-edges;
image-rendering: -o-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
}
@else {
image-rendering: $mode;
}
}
+38
View File
@@ -0,0 +1,38 @@
// Adds keyframes blocks for supported prefixes, removing redundant prefixes in the block's content
@mixin keyframes($name) {
@include _bourbon-deprecate-for-prefixing("keyframes");
$original-prefix-for-webkit: $prefix-for-webkit;
$original-prefix-for-mozilla: $prefix-for-mozilla;
$original-prefix-for-microsoft: $prefix-for-microsoft;
$original-prefix-for-opera: $prefix-for-opera;
$original-prefix-for-spec: $prefix-for-spec;
@if $original-prefix-for-webkit {
@include disable-prefix-for-all();
$prefix-for-webkit: true !global;
@-webkit-keyframes #{$name} {
@content;
}
}
@if $original-prefix-for-mozilla {
@include disable-prefix-for-all();
$prefix-for-mozilla: true !global;
@-moz-keyframes #{$name} {
@content;
}
}
$prefix-for-webkit: $original-prefix-for-webkit !global;
$prefix-for-mozilla: $original-prefix-for-mozilla !global;
$prefix-for-microsoft: $original-prefix-for-microsoft !global;
$prefix-for-opera: $original-prefix-for-opera !global;
$prefix-for-spec: $original-prefix-for-spec !global;
@if $original-prefix-for-spec {
@keyframes #{$name} {
@content;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
@mixin linear-gradient($pos, $g1, $g2: null,
$g3: null, $g4: null,
$g5: null, $g6: null,
$g7: null, $g8: null,
$g9: null, $g10: null,
$fallback: null) {
@include _bourbon-deprecate-for-prefixing("linear-gradient");
// Detect what type of value exists in $pos
$pos-type: type-of(nth($pos, 1));
$pos-spec: null;
$pos-degree: null;
// If $pos is missing from mixin, reassign vars and add default position
@if ($pos-type == color) or (nth($pos, 1) == "transparent") {
$g10: $g9; $g9: $g8; $g8: $g7; $g7: $g6; $g6: $g5;
$g5: $g4; $g4: $g3; $g3: $g2; $g2: $g1; $g1: $pos;
$pos: null;
}
@if $pos {
$positions: _linear-positions-parser($pos);
$pos-degree: nth($positions, 1);
$pos-spec: nth($positions, 2);
}
$full: $g1, $g2, $g3, $g4, $g5, $g6, $g7, $g8, $g9, $g10;
// Set $g1 as the default fallback color
$fallback-color: nth($g1, 1);
// If $fallback is a color use that color as the fallback color
@if (type-of($fallback) == color) or ($fallback == "transparent") {
$fallback-color: $fallback;
}
background-color: $fallback-color;
background-image: -webkit-linear-gradient($pos-degree $full); // Safari 5.1+, Chrome
background-image: unquote("linear-gradient(#{$pos-spec}#{$full})");
}
+12
View File
@@ -0,0 +1,12 @@
@mixin perspective($depth: none) {
@include _bourbon-deprecate-for-prefixing("perspective");
// none | <length>
@include prefixer(perspective, $depth, webkit moz spec);
}
@mixin perspective-origin($value: 50% 50%) {
@include _bourbon-deprecate-for-prefixing("perspective-origin");
@include prefixer(perspective-origin, $value, webkit moz spec);
}
+10
View File
@@ -0,0 +1,10 @@
@mixin placeholder {
@include _bourbon-deprecate-for-prefixing("placeholder");
$placeholders: ":-webkit-input" ":-moz" "-moz" "-ms-input";
@each $placeholder in $placeholders {
&:#{$placeholder}-placeholder {
@content;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
// Requires Sass 3.1+
@mixin radial-gradient($g1, $g2,
$g3: null, $g4: null,
$g5: null, $g6: null,
$g7: null, $g8: null,
$g9: null, $g10: null,
$pos: null,
$shape-size: null,
$fallback: null) {
@include _bourbon-deprecate-for-prefixing("radial-gradient");
$data: _radial-arg-parser($g1, $g2, $pos, $shape-size);
$g1: nth($data, 1);
$g2: nth($data, 2);
$pos: nth($data, 3);
$shape-size: nth($data, 4);
$full: $g1, $g2, $g3, $g4, $g5, $g6, $g7, $g8, $g9, $g10;
// Strip deprecated cover/contain for spec
$shape-size-spec: _shape-size-stripper($shape-size);
// Set $g1 as the default fallback color
$first-color: nth($full, 1);
$fallback-color: nth($first-color, 1);
@if (type-of($fallback) == color) or ($fallback == "transparent") {
$fallback-color: $fallback;
}
// Add Commas and spaces
$shape-size: if($shape-size, "#{$shape-size}, ", null);
$pos: if($pos, "#{$pos}, ", null);
$pos-spec: if($pos, "at #{$pos}", null);
$shape-size-spec: if(($shape-size-spec != " ") and ($pos == null), "#{$shape-size-spec}, ", "#{$shape-size-spec} ");
background-color: $fallback-color;
background-image: -webkit-radial-gradient(#{$pos}#{$shape-size}#{$full});
background-image: radial-gradient(#{$shape-size-spec}#{$pos-spec}#{$full});
}
+44
View File
@@ -0,0 +1,44 @@
@charset "UTF-8";
/// Outputs the spec and prefixed versions of the `::selection` pseudo-element.
///
/// @param {Bool} $current-selector [false]
/// If set to `true`, it takes the current element into consideration.
///
/// @example scss - Usage
/// .element {
/// @include selection(true) {
/// background-color: #ffbb52;
/// }
/// }
///
/// @example css - CSS Output
/// .element::-moz-selection {
/// background-color: #ffbb52;
/// }
///
/// .element::selection {
/// background-color: #ffbb52;
/// }
@mixin selection($current-selector: false) {
@include _bourbon-deprecate-for-prefixing("selection");
@if $current-selector {
&::-moz-selection {
@content;
}
&::selection {
@content;
}
} @else {
::-moz-selection {
@content;
}
::selection {
@content;
}
}
}
+27
View File
@@ -0,0 +1,27 @@
@mixin text-decoration($value) {
@include _bourbon-deprecate-for-prefixing("text-decoration");
// <text-decoration-line> || <text-decoration-style> || <text-decoration-color>
@include prefixer(text-decoration, $value, moz);
}
@mixin text-decoration-line($line: none) {
@include _bourbon-deprecate-for-prefixing("text-decoration-line");
// none || underline || overline || line-through
@include prefixer(text-decoration-line, $line, moz);
}
@mixin text-decoration-style($style: solid) {
@include _bourbon-deprecate-for-prefixing("text-decoration-style");
// solid || double || dotted || dashed || wavy
@include prefixer(text-decoration-style, $style, moz webkit);
}
@mixin text-decoration-color($color: currentColor) {
@include _bourbon-deprecate-for-prefixing("text-decoration-color");
// currentColor || <color>
@include prefixer(text-decoration-color, $color, moz);
}
+21
View File
@@ -0,0 +1,21 @@
@mixin transform($property: none) {
@include _bourbon-deprecate-for-prefixing("transform");
// none | <transform-function>
@include prefixer(transform, $property, webkit moz ms o spec);
}
@mixin transform-origin($axes: 50%) {
@include _bourbon-deprecate-for-prefixing("transform-origin");
// x-axis - left | center | right | length | %
// y-axis - top | center | bottom | length | %
// z-axis - length
@include prefixer(transform-origin, $axes, webkit moz ms o spec);
}
@mixin transform-style($style: flat) {
@include _bourbon-deprecate-for-prefixing("transform-style");
@include prefixer(transform-style, $style, webkit moz ms o spec);
}
+81
View File
@@ -0,0 +1,81 @@
// Shorthand mixin. Supports multiple parentheses-deliminated values for each variable.
// Example: @include transition (all 2s ease-in-out);
// @include transition (opacity 1s ease-in 2s, width 2s ease-out);
// @include transition-property (transform, opacity);
@mixin transition($properties...) {
@include _bourbon-deprecate-for-prefixing("transition");
// Fix for vendor-prefix transform property
$needs-prefixes: false;
$webkit: ();
$moz: ();
$spec: ();
// Create lists for vendor-prefixed transform
@each $list in $properties {
@if nth($list, 1) == "transform" {
$needs-prefixes: true;
$list1: -webkit-transform;
$list2: -moz-transform;
$list3: ();
@each $var in $list {
$list3: join($list3, $var);
@if $var != "transform" {
$list1: join($list1, $var);
$list2: join($list2, $var);
}
}
$webkit: append($webkit, $list1);
$moz: append($moz, $list2);
$spec: append($spec, $list3);
} @else {
$webkit: append($webkit, $list, comma);
$moz: append($moz, $list, comma);
$spec: append($spec, $list, comma);
}
}
@if $needs-prefixes {
-webkit-transition: $webkit;
-moz-transition: $moz;
transition: $spec;
} @else {
@if length($properties) >= 1 {
@include prefixer(transition, $properties, webkit moz spec);
} @else {
$properties: all 0.15s ease-out 0s;
@include prefixer(transition, $properties, webkit moz spec);
}
}
}
@mixin transition-property($properties...) {
@include _bourbon-deprecate-for-prefixing("transition-property");
-webkit-transition-property: transition-property-names($properties, "webkit");
-moz-transition-property: transition-property-names($properties, "moz");
transition-property: transition-property-names($properties, false);
}
@mixin transition-duration($times...) {
@include _bourbon-deprecate-for-prefixing("transition-duration");
@include prefixer(transition-duration, $times, webkit moz spec);
}
@mixin transition-timing-function($motions...) {
@include _bourbon-deprecate-for-prefixing("transition-timing-function");
// ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier()
@include prefixer(transition-timing-function, $motions, webkit moz spec);
}
@mixin transition-delay($times...) {
@include _bourbon-deprecate-for-prefixing("transition-delay");
@include prefixer(transition-delay, $times, webkit moz spec);
}
+5
View File
@@ -0,0 +1,5 @@
@mixin user-select($value: none) {
@include _bourbon-deprecate-for-prefixing("user-select");
@include prefixer(user-select, $value, webkit moz ms spec);
}
+16
View File
@@ -0,0 +1,16 @@
@function assign-inputs($inputs, $pseudo: null) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `assign-inputs` is deprecated and will be " +
"removed in 5.0.0.";
}
$list: ();
@each $input in $inputs {
$input: unquote($input);
$input: if($pseudo, $input + ":" + $pseudo, $input);
$list: append($list, $input, comma);
}
@return $list;
}
+25
View File
@@ -0,0 +1,25 @@
@charset "UTF-8";
/// Checks if a list does not contains a value.
///
/// @access private
///
/// @param {List} $list
/// The list to check against.
///
/// @return {Bool}
@function contains-falsy($list) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `contains-falsy` is deprecated and will be " +
"removed in 5.0.0.";
}
@each $item in $list {
@if not $item {
@return true;
}
}
@return false;
}
+31
View File
@@ -0,0 +1,31 @@
@charset "UTF-8";
/// Checks if a list contains a value(s).
///
/// @access private
///
/// @param {List} $list
/// The list to check against.
///
/// @param {List} $values
/// A single value or list of values to check for.
///
/// @example scss - Usage
/// contains($list, $value)
///
/// @return {Bool}
@function contains($list, $values...) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `contains` is deprecated and will be " +
"removed in 5.0.0.";
}
@each $value in $values {
@if type-of(index($list, $value)) != "number" {
@return false;
}
}
@return true;
}
+16
View File
@@ -0,0 +1,16 @@
@charset "UTF-8";
/// Checks for a valid CSS length.
///
/// @param {String} $value
@function is-length($value) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `is-length` is deprecated and will be " +
"removed in 5.0.0.";
}
@return type-of($value) != "null" and (str-slice($value + "", 1, 4) == "calc"
or index(auto inherit initial 0, $value)
or (type-of($value) == "number" and not(unitless($value))));
}
+26
View File
@@ -0,0 +1,26 @@
@charset "UTF-8";
/// Programatically determines whether a color is light or dark.
///
/// @link http://robots.thoughtbot.com/closer-look-color-lightness
///
/// @param {Color (Hex)} $color
///
/// @example scss - Usage
/// is-light($color)
///
/// @return {Bool}
@function is-light($hex-color) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `is-light` is deprecated and will be " +
"removed in 5.0.0.";
}
$-local-red: red(rgba($hex-color, 1));
$-local-green: green(rgba($hex-color, 1));
$-local-blue: blue(rgba($hex-color, 1));
$-local-lightness: ($-local-red * 0.2126 + $-local-green * 0.7152 + $-local-blue * 0.0722) / 255;
@return $-local-lightness > 0.6;
}
+16
View File
@@ -0,0 +1,16 @@
@charset "UTF-8";
/// Checks for a valid number.
///
/// @param {Number} $value
///
/// @require {function} contains
@function is-number($value) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `is-number` is deprecated and will be " +
"removed in 5.0.0.";
}
@return contains("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" 0 1 2 3 4 5 6 7 8 9, $value);
}
+18
View File
@@ -0,0 +1,18 @@
@charset "UTF-8";
/// Checks for a valid CSS size.
///
/// @param {String} $value
///
/// @require {function} contains
/// @require {function} is-length
@function is-size($value) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `is-size` is deprecated and will be " +
"removed in 5.0.0.";
}
@return is-length($value)
or contains("fill" "fit-content" "min-content" "max-content", $value);
}
+69
View File
@@ -0,0 +1,69 @@
// Scaling Variables
$golden: 1.618;
$minor-second: 1.067;
$major-second: 1.125;
$minor-third: 1.2;
$major-third: 1.25;
$perfect-fourth: 1.333;
$augmented-fourth: 1.414;
$perfect-fifth: 1.5;
$minor-sixth: 1.6;
$major-sixth: 1.667;
$minor-seventh: 1.778;
$major-seventh: 1.875;
$octave: 2;
$major-tenth: 2.5;
$major-eleventh: 2.667;
$major-twelfth: 3;
$double-octave: 4;
$modular-scale-ratio: $perfect-fourth !default;
$modular-scale-base: em($em-base) !default;
@function modular-scale($increment, $value: $modular-scale-base, $ratio: $modular-scale-ratio) {
$v1: nth($value, 1);
$v2: nth($value, length($value));
$value: $v1;
// scale $v2 to just above $v1
@while $v2 > $v1 {
$v2: ($v2 / $ratio); // will be off-by-1
}
@while $v2 < $v1 {
$v2: ($v2 * $ratio); // will fix off-by-1
}
// check AFTER scaling $v2 to prevent double-counting corner-case
$double-stranded: $v2 > $v1;
@if $increment > 0 {
@for $i from 1 through $increment {
@if $double-stranded and ($v1 * $ratio) > $v2 {
$value: $v2;
$v2: ($v2 * $ratio);
} @else {
$v1: ($v1 * $ratio);
$value: $v1;
}
}
}
@if $increment < 0 {
// adjust $v2 to just below $v1
@if $double-stranded {
$v2: ($v2 / $ratio);
}
@for $i from $increment through -1 {
@if $double-stranded and ($v1 / $ratio) < $v2 {
$value: $v2;
$v2: ($v2 / $ratio);
} @else {
$v1: ($v1 / $ratio);
$value: $v1;
}
}
}
@return $value;
}
+18
View File
@@ -0,0 +1,18 @@
// Convert pixels to ems
// eg. for a relational value of 12px write em(12) when the parent is 16px
// if the parent is another value say 24px write em(12, 24)
@function em($pxval, $base: $em-base) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `em` is deprecated and will be " +
"removed in 5.0.0.";
}
@if not unitless($pxval) {
$pxval: strip-units($pxval);
}
@if not unitless($base) {
$base: strip-units($base);
}
@return ($pxval / $base) * 1em;
}
+20
View File
@@ -0,0 +1,20 @@
// Convert pixels to rems
// eg. for a relational value of 12px write rem(12)
// Assumes $em-base is the font-size of <html>
@function rem($pxval) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `rem` is deprecated and will be " +
"removed in 5.0.0.";
}
@if not unitless($pxval) {
$pxval: strip-units($pxval);
}
$base: $em-base;
@if not unitless($base) {
$base: strip-units($base);
}
@return ($pxval / $base) * 1rem;
}
+24
View File
@@ -0,0 +1,24 @@
@charset "UTF-8";
/// Mixes a color with black.
///
/// @param {Color} $color
///
/// @param {Number (Percentage)} $percent
/// The amount of black to be mixed in.
///
/// @example scss - Usage
/// .element {
/// background-color: shade(#ffbb52, 60%);
/// }
///
/// @example css - CSS Output
/// .element {
/// background-color: #664a20;
/// }
///
/// @return {Color}
@function shade($color, $percent) {
@return mix(#000, $color, $percent);
}
+22
View File
@@ -0,0 +1,22 @@
@charset "UTF-8";
/// Strips the unit from a number.
///
/// @param {Number (With Unit)} $value
///
/// @example scss - Usage
/// $dimension: strip-units(10em);
///
/// @example css - CSS Output
/// $dimension: 10;
///
/// @return {Number (Unitless)}
@function strip-units($value) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `strip-units` is deprecated and will be " +
"removed in 5.0.0. Use the renamed `strip-unit` function instead.";
}
@return ($value / ($value * 0 + 1));
}
+24
View File
@@ -0,0 +1,24 @@
@charset "UTF-8";
/// Mixes a color with white.
///
/// @param {Color} $color
///
/// @param {Number (Percentage)} $percent
/// The amount of white to be mixed in.
///
/// @example scss - Usage
/// .element {
/// background-color: tint(#6ecaa6, 40%);
/// }
///
/// @example css - CSS Output
/// .element {
/// background-color: #a8dfc9;
/// }
///
/// @return {Color}
@function tint($color, $percent) {
@return mix(#fff, $color, $percent);
}
@@ -0,0 +1,32 @@
// Return vendor-prefixed property names if appropriate
// Example: transition-property-names((transform, color, background), moz) -> -moz-transform, color, background
//************************************************************************//
@function transition-property-names($props, $vendor: false) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `transition-property-names` is deprecated " +
"and will be removed in 5.0.0.";
}
$new-props: ();
@each $prop in $props {
$new-props: append($new-props, transition-property-name($prop, $vendor), comma);
}
@return $new-props;
}
@function transition-property-name($prop, $vendor: false) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `transition-property-name` is deprecated " +
"and will be removed in 5.0.0.";
}
// put other properties that need to be prefixed here aswell
@if $vendor and $prop == transform {
@return unquote('-'+$vendor+'-'+$prop);
}
@else {
@return $prop;
}
}
+32
View File
@@ -0,0 +1,32 @@
@charset "UTF-8";
/// Converts shorthand to the 4-value syntax.
///
/// @param {List} $shorthand
///
/// @example scss - Usage
/// .element {
/// margin: unpack(1em 2em);
/// }
///
/// @example css - CSS Output
/// .element {
/// margin: 1em 2em 1em 2em;
/// }
@function unpack($shorthand) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `unpack` is deprecated and will be " +
"removed in 5.0.0.";
}
@if length($shorthand) == 1 {
@return nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1);
} @else if length($shorthand) == 2 {
@return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 1) nth($shorthand, 2);
} @else if length($shorthand) == 3 {
@return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 3) nth($shorthand, 2);
} @else {
@return $shorthand;
}
}
+26
View File
@@ -0,0 +1,26 @@
//************************************************************************//
// Helper function for str-to-num fn.
// Source: http://sassmeister.com/gist/9647408
//************************************************************************//
@function _convert-units($number, $unit) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `_convert-units` is deprecated and will " +
"be removed in 5.0.0.";
}
$strings: "px", "cm", "mm", "%", "ch", "pica", "in", "em", "rem", "pt", "pc", "ex", "vw", "vh", "vmin", "vmax", "deg", "rad", "grad", "turn";
$units: 1px, 1cm, 1mm, 1%, 1ch, 1pica, 1in, 1em, 1rem, 1pt, 1pc, 1ex, 1vw, 1vh, 1vmin, 1vmax, 1deg, 1rad, 1grad, 1turn;
$index: index($strings, $unit);
@if not $index {
@warn "Unknown unit `#{$unit}`.";
@return false;
}
@if type-of($number) != "number" {
@warn "`#{$number} is not a number`";
@return false;
}
@return $number * nth($units, $index);
}
+103
View File
@@ -0,0 +1,103 @@
@charset "UTF-8";
/// Directional-property mixins are shorthands for writing properties like the following
///
/// @ignore You can also use `false` instead of `null`.
///
/// @param {List} $vals
/// List of directional values
///
/// @example scss - Usage
/// .element {
/// @include border-style(dotted null);
/// @include margin(null 0 10px);
/// }
///
/// @example css - CSS Output
/// .element {
/// border-bottom-style: dotted;
/// border-top-style: dotted;
/// margin-bottom: 10px;
/// margin-left: 0;
/// margin-right: 0;
/// }
///
/// @require {function} contains-falsy
///
/// @return {List}
@function collapse-directionals($vals) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `collapse-directionals` is deprecated and " +
"will be removed in 5.0.0.";
}
$output: null;
$a: nth($vals, 1);
$b: if(length($vals) < 2, $a, nth($vals, 2));
$c: if(length($vals) < 3, $a, nth($vals, 3));
$d: if(length($vals) < 2, $a, nth($vals, if(length($vals) < 4, 2, 4)));
@if $a == 0 { $a: 0; }
@if $b == 0 { $b: 0; }
@if $c == 0 { $c: 0; }
@if $d == 0 { $d: 0; }
@if $a == $b and $a == $c and $a == $d { $output: $a; }
@else if $a == $c and $b == $d { $output: $a $b; }
@else if $b == $d { $output: $a $b $c; }
@else { $output: $a $b $c $d; }
@return $output;
}
/// Output directional properties, for instance `margin`.
///
/// @access private
///
/// @param {String} $pre
/// Prefix to use
/// @param {String} $suf
/// Suffix to use
/// @param {List} $vals
/// List of values
///
/// @require {function} collapse-directionals
/// @require {function} contains-falsy
@mixin directional-property($pre, $suf, $vals) {
@include _bourbon-deprecate("directional-property");
// Property Names
$top: $pre + "-top" + if($suf, "-#{$suf}", "");
$bottom: $pre + "-bottom" + if($suf, "-#{$suf}", "");
$left: $pre + "-left" + if($suf, "-#{$suf}", "");
$right: $pre + "-right" + if($suf, "-#{$suf}", "");
$all: $pre + if($suf, "-#{$suf}", "");
$vals: collapse-directionals($vals);
@if contains-falsy($vals) {
@if nth($vals, 1) { #{$top}: nth($vals, 1); }
@if length($vals) == 1 {
@if nth($vals, 1) { #{$right}: nth($vals, 1); }
} @else {
@if nth($vals, 2) { #{$right}: nth($vals, 2); }
}
@if length($vals) == 2 {
@if nth($vals, 1) { #{$bottom}: nth($vals, 1); }
@if nth($vals, 2) { #{$left}: nth($vals, 2); }
} @else if length($vals) == 3 {
@if nth($vals, 3) { #{$bottom}: nth($vals, 3); }
@if nth($vals, 2) { #{$left}: nth($vals, 2); }
} @else if length($vals) == 4 {
@if nth($vals, 3) { #{$bottom}: nth($vals, 3); }
@if nth($vals, 4) { #{$left}: nth($vals, 4); }
}
} @else {
#{$all}: $vals;
}
}
@@ -0,0 +1,53 @@
// Used for creating the source string for fonts using @font-face
// Reference: http://goo.gl/Ru1bKP
@function font-url-prefixer($asset-pipeline) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `font-url-prefixer` is deprecated and " +
"will be removed in 5.0.0.";
}
@if $asset-pipeline == true {
@return font-url;
} @else {
@return url;
}
}
@function font-source-declaration(
$font-family,
$file-path,
$asset-pipeline,
$file-formats,
$font-url) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `font-source-declaration` is deprecated " +
"and will be removed in 5.0.0.";
}
$src: ();
$formats-map: (
eot: "#{$file-path}.eot?#iefix" format("embedded-opentype"),
woff2: "#{$file-path}.woff2" format("woff2"),
woff: "#{$file-path}.woff" format("woff"),
ttf: "#{$file-path}.ttf" format("truetype"),
svg: "#{$file-path}.svg##{$font-family}" format("svg")
);
@each $key, $values in $formats-map {
@if contains($file-formats, $key) {
$file-path: nth($values, 1);
$font-format: nth($values, 2);
@if $asset-pipeline == true {
$src: append($src, font-url($file-path) $font-format, comma);
} @else {
$src: append($src, url($file-path) $font-format, comma);
}
}
}
@return $src;
}
@@ -0,0 +1,18 @@
@function _gradient-positions-parser($gradient-type, $gradient-positions) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `_gradient-positions-parser` is " +
"deprecated and will be removed in 5.0.0.";
}
@if $gradient-positions
and ($gradient-type == linear)
and (type-of($gradient-positions) != color) {
$gradient-positions: _linear-positions-parser($gradient-positions);
}
@else if $gradient-positions
and ($gradient-type == radial)
and (type-of($gradient-positions) != color) {
$gradient-positions: _radial-positions-parser($gradient-positions);
}
@return $gradient-positions;
}
+30
View File
@@ -0,0 +1,30 @@
// Private function for linear-gradient-parser
@function _linear-angle-parser($image, $first-val, $prefix, $suffix) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `_linear-angle-parser` is " +
"deprecated and will be removed in 5.0.0.";
}
$offset: null;
$unit-short: str-slice($first-val, str-length($first-val) - 2, str-length($first-val));
$unit-long: str-slice($first-val, str-length($first-val) - 3, str-length($first-val));
@if ($unit-long == "grad") or
($unit-long == "turn") {
$offset: if($unit-long == "grad", -100grad * 3, -0.75turn);
}
@else if ($unit-short == "deg") or
($unit-short == "rad") {
$offset: if($unit-short == "deg", -90 * 3, 1.6rad);
}
@if $offset {
$num: _str-to-num($first-val);
@return (
webkit-image: -webkit- + $prefix + ($offset - $num) + $suffix,
spec-image: $image
);
}
}
+46
View File
@@ -0,0 +1,46 @@
@function _linear-gradient-parser($image) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `_linear-gradient-parser` is " +
"deprecated and will be removed in 5.0.0.";
}
$image: unquote($image);
$gradients: ();
$start: str-index($image, "(");
$end: str-index($image, ",");
$first-val: str-slice($image, $start + 1, $end - 1);
$prefix: str-slice($image, 1, $start);
$suffix: str-slice($image, $end, str-length($image));
$has-multiple-vals: str-index($first-val, " ");
$has-single-position: unquote(_position-flipper($first-val) + "");
$has-angle: is-number(str-slice($first-val, 1, 1));
@if $has-multiple-vals {
$gradients: _linear-side-corner-parser($image, $first-val, $prefix, $suffix, $has-multiple-vals);
}
@else if $has-single-position != "" {
$pos: unquote($has-single-position + "");
$gradients: (
webkit-image: -webkit- + $image,
spec-image: $prefix + "to " + $pos + $suffix
);
}
@else if $has-angle {
// Rotate degree for webkit
$gradients: _linear-angle-parser($image, $first-val, $prefix, $suffix);
}
@else {
$gradients: (
webkit-image: -webkit- + $image,
spec-image: $image
);
}
@return $gradients;
}
@@ -0,0 +1,71 @@
@function _linear-positions-parser($pos) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `_linear-positions-parser` is " +
"deprecated and will be removed in 5.0.0.";
}
$type: type-of(nth($pos, 1));
$spec: null;
$degree: null;
$side: null;
$corner: null;
$length: length($pos);
// Parse Side and corner positions
@if ($length > 1) {
@if nth($pos, 1) == "to" { // Newer syntax
$side: nth($pos, 2);
@if $length == 2 { // eg. to top
// Swap for backwards compatibility
$degree: _position-flipper(nth($pos, 2));
}
@else if $length == 3 { // eg. to top left
$corner: nth($pos, 3);
}
}
@else if $length == 2 { // Older syntax ("top left")
$side: _position-flipper(nth($pos, 1));
$corner: _position-flipper(nth($pos, 2));
}
@if ("#{$side} #{$corner}" == "left top") or ("#{$side} #{$corner}" == "top left") {
$degree: _position-flipper(#{$side}) _position-flipper(#{$corner});
}
@else if ("#{$side} #{$corner}" == "right top") or ("#{$side} #{$corner}" == "top right") {
$degree: _position-flipper(#{$side}) _position-flipper(#{$corner});
}
@else if ("#{$side} #{$corner}" == "right bottom") or ("#{$side} #{$corner}" == "bottom right") {
$degree: _position-flipper(#{$side}) _position-flipper(#{$corner});
}
@else if ("#{$side} #{$corner}" == "left bottom") or ("#{$side} #{$corner}" == "bottom left") {
$degree: _position-flipper(#{$side}) _position-flipper(#{$corner});
}
$spec: to $side $corner;
}
@else if $length == 1 {
// Swap for backwards compatibility
@if $type == string {
$degree: $pos;
$spec: to _position-flipper($pos);
}
@else {
$degree: -270 - $pos; //rotate the gradient opposite from spec
$spec: $pos;
}
}
$degree: unquote($degree + ",");
$spec: unquote($spec + ",");
@return $degree $spec;
}
@function _position-flipper($pos) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `_position-flipper` is " +
"deprecated and will be removed in 5.0.0.";
}
@return if($pos == left, right, null)
if($pos == right, left, null)
if($pos == top, bottom, null)
if($pos == bottom, top, null);
}
@@ -0,0 +1,36 @@
// Private function for linear-gradient-parser
@function _linear-side-corner-parser($image, $first-val, $prefix, $suffix, $has-multiple-vals) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `_linear-side-corner-parser` is " +
"deprecated and will be removed in 5.0.0.";
}
$val-1: str-slice($first-val, 1, $has-multiple-vals - 1);
$val-2: str-slice($first-val, $has-multiple-vals + 1, str-length($first-val));
$val-3: null;
$has-val-3: str-index($val-2, " ");
@if $has-val-3 {
$val-3: str-slice($val-2, $has-val-3 + 1, str-length($val-2));
$val-2: str-slice($val-2, 1, $has-val-3 - 1);
}
$pos: _position-flipper($val-1) _position-flipper($val-2) _position-flipper($val-3);
$pos: unquote($pos + "");
// Use old spec for webkit
@if $val-1 == "to" {
@return (
webkit-image: -webkit- + $prefix + $pos + $suffix,
spec-image: $image
);
}
// Bring the code up to spec
@else {
@return (
webkit-image: -webkit- + $image,
spec-image: $prefix + "to " + $pos + $suffix
);
}
}

Some files were not shown because too many files have changed in this diff Show More