Compare commits

..

3 Commits

Author SHA1 Message Date
basti 970b390509 Show contribution history from events 2017-08-14 17:33:38 +02:00
basti b8f4040def Get contributions from minted events (WIP) 2017-08-14 17:15:11 +02:00
basti 8fbecd928b Update deps 2017-08-14 17:14:17 +02:00
191 changed files with 1975 additions and 28196 deletions
+4
View File
@@ -0,0 +1,4 @@
{
"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": true "disableAnalytics": false
} }
-43
View File
@@ -1,43 +0,0 @@
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,11 +14,4 @@
/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
@@ -0,0 +1,32 @@
{
"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
}
+9 -10
View File
@@ -4,23 +4,22 @@ 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
+19 -52
View File
@@ -10,24 +10,22 @@ 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/)
* [Google Chrome](https://google.com/chrome/) * [PhantomJS](http://phantomjs.org/)
## Installation ## Installation
* `git clone git@github.com:67P/kredits-web.git` this repository * `git clone <repository-url>` this repository
* `cd kredits-web` * `cd kredits-web`
* `npm install` * `npm install`
* `bower install`
## Running / Development ## Running / Development
* `ember serve`
* `ember serve` - by default kredits-web connects to the Ethreum Kovan network
* 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
@@ -38,11 +36,6 @@ 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)
@@ -50,61 +43,35 @@ Make use of the many generators for code, try `ember help generate` for more det
### Deploying ### Deploying
_(You need collaborator permissions on the 5apps Deploy project.)_ Specify what it takes to deploy your app.
`npm run deploy`
## Working with locally deployed contracts ## Working with locally deployed contracts
The smart contracts and their JavaScript wrapper library are developed in the For development you might want to run your own development chain with your own development contracts.
[kredits-contracts](https://github.com/67P/kredits-contracts) repo/package. The kredits-contracts helper scripts are available in kredits-web. See [kredits-contracts](https://github.com/67P/kredits-contracts) for more information.
You can run `kredits-web` on your machine, against a local, simulated Ethereum Start your parity node with a KreditsChain:
network, provided e.g. by [ganache](http://truffleframework.com/ganache/) or
[ganache-cli](https://github.com/trufflesuite/ganache-cli).
[kredits-contracts](https://github.com/67P/kredits-contracts) holds all the tools npm run kredits-parity
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: Deploy the contracts to your local chain:
#### 1. IPFS npm run kredits-deploy
Run a local IPFS deamon in offline mode. Run kredits-web with your localy deployed contracts:
* Make sure CORS headers are configured. See [IPFS](#ipfs) for more info. KREDITS_CONTRACT_ADDR=`kredits-inspect -c Kredits -r -w address` \
* `ipfs daemon --offline` TOKEN_CONTRACT_ADDR=`kredits-inspect -c Token -r -w address` \
npm start
#### 2. kredits-contracts You need to have `./node_modules/.bin` in your path for this to work. Otherwise simply copy the contract addresses and set the environment variables manually.
Get your local Ethereum development node running.
* Clone [kredits-contracts](https://github.com/67P/kredits-contracts)
* `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 `kredits-contracts` 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 `kredits-contracts` package
* `npm run start:local` - NETWORK_ID=100 and WEB3_PROVIDER_URL=http://localhost:7545 must be set for local settings
#### 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](https://emberjs.com/) * [ember.js](http://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)
+6 -2
View File
@@ -1,9 +1,13 @@
import Application from '@ember/application'; import Ember from 'ember';
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';
const App = Application.extend({ let App;
Ember.MODEL_FACTORY_INJECTIONS = true;
App = Ember.Application.extend({
modulePrefix: config.modulePrefix, modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix, podModulePrefix: config.podModulePrefix,
Resolver Resolver
+60 -43
View File
@@ -1,64 +1,81 @@
import Component from '@ember/component'; import Ember from 'ember';
import { and, notEmpty } from '@ember/object/computed'; import Contributor from 'kredits-web/models/contributor';
import { inject as injectService } from '@ember/service';
const {
Component,
isPresent,
inject: {
service
},
computed
} = Ember;
export default Component.extend({ export default Component.extend({
kredits: injectService(),
// Default attributes used by reset kredits: service(),
attributes: {
account: null,
name: null,
kind: 'person',
url: null,
github_username: null,
github_uid: null,
wiki_username: null,
isCore: false,
},
didInsertElement() { newContributor: null,
this._super(...arguments); inProgress: false,
this.reset();
},
// TODO: add proper address validation isValidAddress: function() {
isValidAccount: notEmpty('account'), return this.get('kredits.web3')
isValidName: notEmpty('name'), .isAddress(this.get('newContributor.address'));
isValidURL: notEmpty('url'), }.property('kredits.web3', 'newContributor.address'),
isValidGithubUID: notEmpty('github_uid'),
isValidGithubUsername: notEmpty('github_username'), isValidName: function() {
isValidWikiUsername: notEmpty('wiki_username'), return isPresent(this.get('newContributor.name'));
isValid: and( }.property('newContributor.name'),
'isValidAccount',
isValidURL: function() {
return isPresent(this.get('newContributor.url'));
}.property('newContributor.url'),
isValidGithubUID: function() {
return isPresent(this.get('newContributor.github_uid'));
}.property('newContributor.github_uid'),
isValidGithubUsername: function() {
return isPresent(this.get('newContributor.github_username'));
}.property('newContributor.github_username'),
isValidWikiUsername: function() {
return isPresent(this.get('newContributor.wiki_username'));
}.property('newContributor.wiki_username'),
isValid: computed.and(
'isValidAddress',
'isValidName', 'isValidName',
'isValidGithubUID' 'isValidGithubUID'
), ),
reset: function() { reset: function() {
this.setProperties(this.attributes); this.setProperties({
newContributor: Contributor.create({ kind: 'person' }),
inProgress: false
});
}, },
actions: { actions: {
submit() {
if (!this.isValid) { save() {
alert('Invalid data. Please review and try again.'); if (!this.get('contractInteractionEnabled')) {
alert('Only core team members can add new contributors. Please ask someone to set you up.');
return; return;
} }
let attributes = Object.keys(this.attributes); if (this.get('isValid')) {
let contributor = this.getProperties(attributes); this.set('inProgress', true);
let saved = this.save(contributor);
// The promise handles inProgress this.get('kredits').addContributor(this.get('newContributor')).then(contributor => {
this.set('inProgress', saved); this.reset();
this.get('contributors').pushObject(contributor);
saved.then(() => { window.scroll(0,0);
this.reset(); });
window.scroll(0,0); } else {
window.alert('Contributor added.'); alert('Invalid data. Please review and try again.');
}); }
} }
} }
}); });
+14 -19
View File
@@ -1,64 +1,59 @@
<form {{action "submit" on="submit"}}> <form {{action "save" on="submit"}}>
<p> <p>
{{input name="is-core" {{input type="checkbox" name="is-core" id="is-core" checked=newContributor.isCore}}
type="checkbox"
id="is-core"
checked=isCore}}
<label for="is-core" class="checkbox"> <label for="is-core" class="checkbox">
Core team member (can add contributors) Core team member (can add contributors)
</label> </label>
</p> </p>
<p> <p>
{{input name="account" {{input name="address"
type="text" type="text"
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4" placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
value=account value=newContributor.address
class=(if isValidAccount 'valid' '')}} class=(if isValidAddress 'valid' '')}}
</p> </p>
<p> <p>
<select required onchange={{action (mut kind) value="target.value"}}> <select required onchange={{action (mut newContributor.kind) value="target.value"}}>
<option value="person" selected={{eq kind "person"}}>Person</option> <option value="person" selected={{eq newContributor.kind "person"}}>Person</option>
<option value="organization" selected={{eq kind "organization"}}>Organization</option> <option value="organization" selected={{eq newContributor.kind "organization"}}>Organization</option>
</select> </select>
</p> </p>
<p> <p>
{{input name="name" {{input name="name"
type="text" type="text"
placeholder="Name" placeholder="Name"
value=name value=newContributor.name
class=(if isValidName 'valid' '')}} class=(if isValidName 'valid' '')}}
</p> </p>
<p> <p>
{{input name="url" {{input name="url"
type="text" type="text"
placeholder="URL" placeholder="URL"
value=url value=newContributor.url
class=(if isValidURL 'valid' '')}} class=(if isValidURL 'valid' '')}}
</p> </p>
<p> <p>
{{input name="github_uid" {{input name="github_uid"
type="text" type="text"
placeholder="GitHub UID (123)" placeholder="GitHub UID (123)"
value=github_uid value=newContributor.github_uid
class=(if isValidGithubUID 'valid' '')}} class=(if isValidGithubUID 'valid' '')}}
</p> </p>
<p> <p>
{{input name="github_username" {{input name="github_username"
type="text" type="text"
placeholder="GitHub username" placeholder="GitHub username"
value=github_username value=newContributor.github_username
class=(if isValidGithubUsername 'valid' '')}} class=(if isValidGithubUsername 'valid' '')}}
</p> </p>
<p> <p>
{{input name="wiki_username" {{input name="wiki_username"
type="text" type="text"
placeholder="Wiki Username" placeholder="Wiki Username"
value=wiki_username value=newContributor.wiki_username
class=(if isValidWikiUsername 'valid' '')}} class=(if isValidWikiUsername 'valid' '')}}
</p> </p>
<p class="actions"> <p class="actions">
{{input type="submit" {{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
disabled=(is-pending inProgress)
value=(if (is-pending inProgress) 'Processing' 'Save')}}
</p> </p>
</form> </form>
+50 -41
View File
@@ -1,57 +1,66 @@
import Component from '@ember/component'; import Ember from 'ember';
import { computed } from '@ember/object';
import { and, notEmpty } from '@ember/object/computed'; const {
Component,
isPresent,
inject: {
service
},
computed
} = Ember;
export default Component.extend({ export default Component.extend({
// Default attributes used by reset
attributes: {
contributorId: null,
kind: 'community',
amount: null,
description: null,
url: null,
},
didInsertElement() { kredits: service(),
this._super(...arguments);
this.reset();
},
contributors: [], proposal: null,
contributors: null,
inProgress: false,
isValidContributor: notEmpty('contributorId'), isValidRecipient: computed('proposal.recipientAddress', function() {
isValidAmount: computed('amount', function() { return this.get('kredits.web3').isAddress(this.get('proposal.recipientAddress'));
return parseInt(this.amount, 10) > 0;
}), }),
isValidDescription: notEmpty('description'),
isValidUrl: notEmpty('url'),
isValid: and('isValidContributor',
'isValidAmount',
'isValidDescription'),
reset: function() { isValidAmount: computed('proposal.amount', function() {
this.setProperties(this.attributes); return parseInt(this.get('proposal.amount'), 10) > 0;
}, }),
isValidUrl: computed('proposal.url', function() {
return isPresent(this.get('proposal.url'));
}),
isValidDescription: computed('proposal.description', function() {
return isPresent(this.get('proposal.description'));
}),
isValid: computed.and('isValidRecipient',
'isValidAmount',
'isValidDescription'),
actions: { actions: {
submit() { save() {
if (!this.isValid) { if (! this.get('isValid')) {
alert('Invalid data. Please review and try again.'); alert('Invalid data. Please review and try again.');
return; return false;
} }
this.set('inProgress', true);
let proposal = this.get('proposal');
let attributes = Object.keys(this.attributes); // Set the recipient's IPFS profile hash so it can be used in the
let proposal = this.getProperties(attributes); // contribution object (which is to be stored in IPFS as well)
let saved = this.save(proposal); let contributor = this.get('contributors').findBy('address', proposal.get('recipientAddress'));
proposal.set('recipientProfile', contributor.get('ipfsHash'));
// The promise handles inProgress this.get('kredits').addProposal(proposal)
this.set('inProgress', saved); .then(() => {
this.attrs.onSave();
saved.then(() => { }).catch((error) => {
this.reset(); Ember.Logger.error('[add-proposal] error creating the proposal', error);
window.scroll(0,0); alert('Something went wrong.');
window.alert('Proposal added.'); }).finally(() => {
}); this.set('inProgress', false);
});
} }
} }
}); });
+13 -15
View File
@@ -1,43 +1,41 @@
<form {{action "submit" on="submit"}}> <form {{action "save" on="submit"}}>
<p> <p>
<select required onchange={{action (mut contributorId) value="target.value"}}> <select required onchange={{action (mut proposal.recipientAddress) value="target.value"}}>
<option value="" selected disabled hidden>Contributor</option> <option value="" selected disabled hidden>Contributor</option>
{{#each contributors as |contributor|}} {{#each contributors as |contributor|}}
<option value={{contributor.id}} selected={{eq contributorId contributor.id}}>{{contributor.github_username}}</option> <option value={{contributor.address}} selected={{eq proposal.recipientAddress contributor.address}}>{{contributor.github_username}}</option>
{{/each}} {{/each}}
</select> </select>
</p> </p>
<p> <p>
<select required onchange={{action (mut kind) value="target.value"}}> <select required onchange={{action (mut proposal.kind) value="target.value"}}>
<option value="community" selected={{eq kind "community"}}>Community</option> <option value="community" selected={{eq proposal.kind "community"}}>Community</option>
<option value="design" selected={{eq kind "design"}}>Design</option> <option value="design" selected={{eq proposal.kind "design"}}>Design</option>
<option value="dev" selected={{eq kind "dev"}}>Development</option> <option value="dev" selected={{eq proposal.kind "dev"}}>Development</option>
<option value="docs" selected={{eq kind "docs"}}>Documentation</option> <option value="docs" selected={{eq proposal.kind "docs"}}>Documentation</option>
<option value="ops" selected={{eq kind "ops"}}>IT Operations</option> <option value="ops" selected={{eq proposal.kind "ops"}}>IT Operations</option>
</select> </select>
</p> </p>
<p> <p>
{{input type="text" {{input type="text"
placeholder="100" placeholder="100"
value=amount value=proposal.amount
class=(if isValidAmount 'valid' '')}} class=(if isValidAmount 'valid' '')}}
</p> </p>
<p> <p>
{{input type="text" {{input type="text"
placeholder="Description" placeholder="Description"
value=description value=proposal.description
class=(if isValidDescription 'valid' '')}} class=(if isValidDescription 'valid' '')}}
</p> </p>
<p> <p>
{{input type="text" {{input type="text"
placeholder="URL (optional)" placeholder="URL (optional)"
value=url value=proposal.url
class=(if isValidUrl 'valid' '')}} class=(if isValidUrl 'valid' '')}}
</p> </p>
<p class="actions"> <p class="actions">
{{input type="submit" {{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
disabled=(is-pending inProgress)
value=(if (is-pending inProgress) 'Processing' 'Save')}}
{{#link-to 'index'}}Back{{/link-to}} {{#link-to 'index'}}Back{{/link-to}}
</p> </p>
</form> </form>
@@ -1,59 +0,0 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
let categoryColors = {
community: "#fb6868",
design: "#fbe468",
dev: "#e068fb",
docs: "#97fb68",
ops: "#8f68fb",
}
export default Component.extend({
contributions: null,
chartData: computed('contributions', function() {
let kredits = this.get('contributions')
.map(c => {
return { kind: c.kind, amount: c.amount }
}).reduce(function (kinds, c) {
kinds[c.kind] = (kinds[c.kind] || 0) + c.amount;
return kinds;
}, {});
return {
datasets: [{
data: [
kredits['community'],
kredits['design'],
kredits['dev'],
kredits['ops'],
kredits['docs'],
],
borderColor: [
categoryColors.community,
categoryColors.design,
categoryColors.dev,
categoryColors.ops,
categoryColors.docs,
],
borderWidth: 1
}],
labels: [
'Community',
'Design',
'Development',
'Operations & Infrastructure',
'Documentation'
],
}
}),
chartOptions: {
legend: {
display: false
}
}
});
@@ -1,7 +0,0 @@
<div class="chart">
{{ember-chart type='doughnut'
data=chartData
options=chartOptions
width=200
height=200}}
</div>
@@ -0,0 +1,16 @@
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'ul',
classNames: ['contribution-list'],
contributionsWithContributors: function() {
return this.get('contributions').map((c) => {
c.set('contributor', this.get('contributors')
.findBy('address', c.get('recipientAddress')));
return c;
});
}.property('contributions.@each')
});
@@ -0,0 +1,7 @@
{{#each contributionsWithContributors as |contribution|}}
<li title="({{contribution.kind}}) {{contribution.description}}">
<span class="category {{contribution.kind}}">♥</span>
<span class="amount">{{contribution.amount}}</span><span class="symbol">₭S</span>
for <span class="recipient">{{contribution.contributor.name}}</span>
</li>
{{/each}}
+3 -3
View File
@@ -1,6 +1,6 @@
import Component from '@ember/component'; import Ember from 'ember';
export default Component.extend({ export default Ember.Component.extend({
tagName: 'table', tagName: 'table',
classNames: 'contributor-list', classNames: 'contributor-list',
@@ -12,7 +12,7 @@ export default Component.extend({
if (contributor.get('showMetadata')) { if (contributor.get('showMetadata')) {
contributor.set('showMetadata', false); contributor.set('showMetadata', false);
} else { } else {
this.contributors.setEach('showMetadata', false); this.get('contributors').setEach('showMetadata', false);
contributor.set('showMetadata', true); contributor.set('showMetadata', true);
} }
} }
+2 -5
View File
@@ -6,21 +6,18 @@
{{contributor.name}} {{contributor.name}}
</td> </td>
<td class="kredits"> <td class="kredits">
<span class="amount">{{contributor.balance}}</span> <span class="amount">{{contributor.kredits}}</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'}}"> <tr class="metadata {{if contributor.isCurrentUser 'current-user'}} {{if contributor.showMetadata 'visible'}}">
<td colspan="2"> <td colspan="2">
<ul> <ul>
<li><a href="https://testnet.etherscan.io/address/{{contributor.account}}">Inspect Ethereum transactions</a></li> <li><a href="https://testnet.etherscan.io/address/{{contributor.address}}">Inspect Ethereum transactions</a></li>
{{#if contributor.ipfsHash}} {{#if contributor.ipfsHash}}
<li><a href="https://ipfs.io/ipfs/{{contributor.ipfsHash}}">Inspect IPFS profile</a></li> <li><a href="https://ipfs.io/ipfs/{{contributor.ipfsHash}}">Inspect IPFS profile</a></li>
{{/if}} {{/if}}
</ul> </ul>
{{#if contributor.showMetadata}}
<pre>{{contributor.ipfsData}}</pre>
{{/if}}
</td> </td>
</tr> </tr>
{{/each}} {{/each}}
+2 -2
View File
@@ -1,6 +1,6 @@
import Component from '@ember/component'; import Ember from 'ember';
export default Component.extend({ export default Ember.Component.extend({
classNames: ['loading-spinner'] classNames: ['loading-spinner']
+4 -4
View File
@@ -1,6 +1,6 @@
import Component from '@ember/component'; import Ember from 'ember';
export default Component.extend({ export default Ember.Component.extend({
tagName: 'ul', tagName: 'ul',
classNames: ['proposal-list'], classNames: ['proposal-list'],
@@ -8,8 +8,8 @@ export default Component.extend({
actions: { actions: {
confirm(proposalId) { confirm(proposalId) {
if (this.contractInteractionEnabled) { if (this.get('contractInteractionEnabled')) {
this.confirmProposal(proposalId); this.sendAction('confirmAction', proposalId);
} else { } else {
window.alert('Only members can vote on proposals. Please ask someone to set you up.'); window.alert('Only members can vote on proposals. Please ask someone to set you up.');
} }
+7 -17
View File
@@ -1,20 +1,10 @@
{{#each proposals as |proposal|}} {{#each proposals as |proposal|}}
<li data-proposal-id={{proposal.id}} class={{if proposal.isExecuted "confirmed" "unconfirmed"}}> <li data-proposal-id={{proposal.id}} title="({{proposal.kind}}) {{proposal.description}}">
<p class="meta"> <span class="category {{proposal.kind}}">♥</span>
<span class="category {{proposal.kind}}">♥ ({{proposal.kind}})</span> <span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
<span class="recipient">{{proposal.contributor.name}}:</span> for <span class="recipient">{{proposal.recipientName}}</span>
</p> <span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
<p class="kredits-amount">
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span> {{#unless proposal.executed}}<button {{action "confirm" proposal.id}}>+1</button>{{/unless}}
</p>
<p class="description">
<span class="description">{{proposal.description}}</span>
</p>
<p class="voting">
{{#unless proposal.isExecuted}}
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
<button {{action "confirm" proposal.id}}>+1</button>
{{/unless}}
</p>
</li> </li>
{{/each}} {{/each}}
+2 -4
View File
@@ -1,6 +1,4 @@
import Controller from '@ember/controller'; import Ember from 'ember';
import { inject as injectService } from '@ember/service';
export default Controller.extend({ export default Ember.Controller.extend({
kredits: injectService(),
}); });
+138 -27
View File
@@ -1,37 +1,148 @@
import Controller from '@ember/controller'; import Ember from 'ember';
import { alias, filter, filterBy, sort } from '@ember/object/computed'; import Proposal from 'kredits-web/models/proposal';
import { inject as injectService } from '@ember/service';
export default Controller.extend({ const {
kredits: injectService(), computed,
inject: {
service
}
} = Ember;
contributors: alias('kredits.contributors'), export default Ember.Controller.extend({
contributorsWithKredits: filter('contributors', function(contributor) {
return contributor.get('balance') !== 0; kredits: service(),
}),
contributorsSorting: ['balance:desc'], contractInteractionEnabled: computed.alias('kredits.web3Provided'),
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.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).name);
return p;
});
return proposals;
}.property('model.proposals.[]', 'model.proposals.@each.executed', 'model.contributors.[]'),
proposalsClosed: function() {
let proposals = this.get('model.proposals')
.filterBy('executed', true)
.map(p => {
p.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).name);
return p;
});
return proposals;
}.property('model.proposals.[]', 'model.proposals.@each.executed', 'model.contributors.[]'),
proposals: alias('kredits.proposals'),
proposalsOpen: filterBy('proposals', 'isExecuted', false),
proposalsClosed: filterBy('proposals', 'isExecuted', true),
proposalsSorting: ['id:desc'], proposalsSorting: ['id:desc'],
proposalsClosedSorted: sort('proposalsClosed', 'proposalsSorting'), proposalsClosedSorted: Ember.computed.sort('proposalsClosed', 'proposalsSorting'),
proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'), proposalsOpenSorted: Ember.computed.sort('proposalsOpen', 'proposalsSorting'),
contributorsWithKredits: function() {
return this.get('model.contributors').filter(c => {
return c.get('kredits') !== 0;
});
}.property('model.contributors.@each.kredits'),
contributorsSorting: ['kredits:desc'],
contributorsSorted: Ember.computed.sort('contributorsWithKredits', 'contributorsSorting'),
watchContractEvents: function() {
let events = this.get('kredits.kreditsContract')
.allEvents(/* [additionalFilterObject], */);
events.watch((error, data) => {
Ember.Logger.debug('[index] Received contract event', data);
switch (data.event) {
case 'ProposalCreated':
this._handleProposalCreated(data);
break;
case 'ProposalExecuted':
this._handleProposalExecuted(data);
break;
case 'ProposalVoted':
this._handleProposalVoted(data);
break;
case 'Transfer':
this._handleTransfer(data);
break;
}
});
}.on('init'),
_handleProposalCreated(data) {
if (Ember.isPresent(this.get('model.proposals')
.findBy('id', data.args.id.toNumber()))) {
Ember.Logger.debug('[index] proposal exists, not adding from event');
return false;
}
let proposal = Proposal.create({
id: data.args.id.toNumber(),
creatorAddress: data.args.creator,
recipientAddress: data.args.recipient,
recipientName: null,
votesCount: 0,
votesNeeded: 2,
amount: data.args.amount.toNumber(),
executed: false,
url: data.args.url,
ipfsHash: data.args.ipfsHash
});
this.get('model.proposals').pushObject(proposal);
},
_handleProposalExecuted(data) {
if (this.get('model.proposals')
.findBy('id', data.args.id.toNumber())
.get('executed')) {
Ember.Logger.debug('[index] proposal already executed, not adding from event');
return false;
}
this.get('model.proposals')
.findBy('id', data.args.id.toNumber())
.setProperties({
'executed': true,
'votesCount': 2 // TODO use real count
});
this.get('model.contributors')
.findBy('address', data.args.recipient)
.incrementProperty('kredits', data.args.amount.toNumber());
},
_handleProposalVoted(data) {
this.get('model.proposals')
.findBy('id', data.args.id.toNumber())
.incrementProperty('votesCount', 1);
},
_handleTransfer(data) {
this.get('model.contributors')
.findBy('address', data.args.from)
.incrementProperty('kredits', - data.args.value.toNumber());
this.get('model.contributors')
.findBy('address', data.args.to)
.incrementProperty('kredits', data.args.value.toNumber());
},
actions: { actions: {
confirmProposal(proposalId) {
this.kredits.vote(proposalId).then(transaction => {
window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
});
},
save(contributor) { confirmProposal(proposalId) {
return this.kredits.addContributor(contributor) this.get('kredits').vote(proposalId).then(transactionId => {
.then((contributor) => { window.confirm('Vote submitted to Ethereum blockhain: '+transactionId);
this.contributors.pushObject(contributor); });
return contributor;
});
} }
} }
}); });
+26 -21
View File
@@ -1,24 +1,29 @@
import Controller from '@ember/controller'; import Ember from 'ember';
import { alias, filterBy } from '@ember/object/computed'; import QueryParams from 'ember-parachute';
import { inject as injectService } from '@ember/service';
export default Controller.extend({ export const queryParams = new QueryParams({
kredits: injectService(), recipient: {
defaultValue: ''
contributors: alias('kredits.contributors'), },
minedContributors: filterBy('contributors', 'id'), amount: {
defaultValue: ''
actions: { },
save(proposal) { url: {
// 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. defaultValue: ''
let contributor = this.contributors.findBy('id', proposal.contributorId); },
proposal.contributorIpfsHash = contributor.get('ipfsHash'); ipfsHash: {
defaultValue: ''
return this.kredits.addProposal(proposal)
.then((proposal) => {
this.transitionToRoute('index');
return proposal;
});
}
} }
}); });
export default Ember.Controller.extend(queryParams.Mixin, {
contributors: null,
actions: {
onSave() {
this.transitionToRoute('index');
}
}
});
+2 -2
View File
@@ -9,8 +9,8 @@
{{content-for "head"}} {{content-for "head"}}
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css"> <link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/kredits-web.css"> <link rel="stylesheet" href="{{rootURL}}assets/kredits-web.css">
{{content-for "head-footer"}} {{content-for "head-footer"}}
+20
View File
@@ -0,0 +1,20 @@
/**
* This pauses the app's boot process until the window load event is fired.
* It allows the user provided Web3 instance (e.g. Metamask, Mist Browser)
* to be inserted before we try to use it.
*/
export default {
name: 'defer-loading',
initialize: function(application) {
// Load event already fired, so web3 should be loaded if available
if (window.windowLoadComplete) { return true; }
// Pause app loading
application.deferReadiness();
window.addEventListener('load', function() {
// Continue app loading
application.advanceReadiness();
});
}
};
+53
View File
@@ -0,0 +1,53 @@
import Ember from 'ember';
export default Ember.Object.extend({
// blockNumber: null,
// blockHash: null,
// transactionHash: null,
recipientAddress: null,
contributorId: null,
ipfsHash: null,
amount: null,
contributor: null,
contribution: null,
kind: null,
description: null,
url: null,
details: null,
/**
* Loads the contribution details from IPFS and sets local instance
* properties from it
*
* @method
* @public
*/
loadDetails(ipfs) {
let promise = new Ember.RSVP.Promise((resolve, reject) => {
ipfs.getFile(this.get('ipfsHash')).then(content => {
let contributionJSON = JSON.parse(content);
let contribution = Ember.Object.create(contributionJSON);
this.setProperties({
kind: contribution.get('kind'),
description: contribution.get('description'),
url: contribution.get('url')
});
// TODO load details
// let details = profile.get('accounts');
Ember.Logger.debug('[proposal] loaded contribution details', contributionJSON);
resolve();
}).catch((err) => {
Ember.Logger.error('[proposal] error trying to load contribution details', this.get('ipfsHash'), err);
reject(err);
});
});
return promise;
},
});
+108 -20
View File
@@ -1,31 +1,119 @@
import { computed } from '@ember/object'; import Ember from 'ember';
import EmberObject from '@ember/object';
import bignumber from 'kredits-web/utils/cps/bignumber';
export default EmberObject.extend({ const {
// Contract isPresent,
id: bignumber('idRaw', 'toString'), } = Ember;
account: null,
balance: bignumber('balanceRaw', 'toNumber'),
isCore: false,
ipfsHash: null,
// IPFS export default Ember.Object.extend({
kind: null,
address: null,
name: null, name: null,
kind: null,
url: null, url: null,
github_username: null, github_username: null,
github_uid: null, github_uid: null,
wiki_username: null, wiki_username: null,
ipfsData: '', ipfsHash: null,
kredits: null,
// Deprecated isCore: false,
isCurrentUser: false, isCurrentUser: false,
avatarURL: computed('github_uid', function() { avatarURL: function() {
let github_uid = this.github_uid; return `https\:\/\/avatars2.githubusercontent.com/u/${this.get('github_uid')}?v=3&s=128`;
if (github_uid) { }.property('github_uid'),
return `https://avatars2.githubusercontent.com/u/${github_uid}?v=3&s=128`;
/**
* Loads the contributor's profile data from IPFS and sets local instance
* properties from it
*
* @method
* @public
*/
loadProfile(ipfs) {
let promise = new Ember.RSVP.Promise((resolve, reject) => {
ipfs.getFile(this.get('ipfsHash')).then(content => {
let profileJSON = JSON.parse(content);
let profile = Ember.Object.create(profileJSON);
this.setProperties({
name: profile.get('name'),
kind: profile.get('kind')
});
let accounts = profile.get('accounts');
let github = accounts.findBy('site', 'github.com');
let wiki = accounts.findBy('site', 'wiki.kosmos.org');
if (isPresent(github)) {
this.setProperties({
github_username: github.username,
github_uid: github.uid,
});
}
if (isPresent(wiki)) {
this.setProperties({
wiki_username: wiki.username
});
}
Ember.Logger.debug('[contributor] loaded contributor profile', profile);
resolve();
}).catch((err) => {
Ember.Logger.error('[contributor] error trying to load contributor profile', this.get('ipfsHash'), err);
reject(err);
});
});
return promise;
},
/**
* Creates a JSON-LD object of the contributor, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
*
* @method
* @public
*/
toJSON() {
let contributor = {
"@context": "https://schema.kosmos.org",
"@type": "Contributor",
"kind": this.get('kind'),
"name": this.get('name'),
"accounts": []
};
if (Ember.isPresent(this.get('url'))) {
contributor["url"] = this.get('url');
} }
}), if (Ember.isPresent(this.get('github_uid'))) {
contributor.accounts.push({
"site": "github.com",
"uid": this.get('github_uid'),
"username": this.get('github_username'),
"url": `https://github.com/${this.get('github_username')}`
});
}
if (Ember.isPresent(this.get('wiki_username'))) {
contributor.accounts.push({
"site": "wiki.kosmos.org",
"username": this.get('wiki_username'),
"url": `https://wiki.kosmos.org/User:${this.get('wiki_username')}`
});
}
return contributor;
},
/**
* Returns the JSON-LD representation of the model as a string
*
* @method
* @public
*/
serialize() {
return JSON.stringify(this.toJSON());
}
}); });
+96 -19
View File
@@ -1,25 +1,102 @@
import EmberObject from '@ember/object'; import Ember from 'ember';
import { alias } from '@ember/object/computed';
import bignumber from 'kredits-web/utils/cps/bignumber';
export default EmberObject.extend({ const {
// Contract isPresent,
id: bignumber('idRaw', 'toString'), isEmpty,
creatorAccount: null, } = Ember;
contributorId: bignumber('contributorIdRaw', 'toString'),
amount: bignumber('amountRaw', 'toNumber'), export default Ember.Object.extend({
votesCount: bignumber('votesCountRaw', 'toNumber'),
votesNeeded: bignumber('votesNeededRaw', 'toNumber'), id: null,
creatorAddress: null,
recipientAddress: null,
recipientName: null,
recipientProfile: null,
votesCount: null,
votesNeeded: null,
amount: null,
executed: null, executed: null,
ipfsHash: null, contribution: null,
// Shortcuts
isExecuted: alias('executed'),
// IPFS
kind: null, kind: null,
description: null, description: null,
details: {},
url: null, url: null,
ipfsData: '', details: null,
ipfsHash: null,
/**
* Loads the contribution details from IPFS and sets local instance
* properties from it
*
* @method
* @public
*/
loadContribution(ipfs) {
let promise = new Ember.RSVP.Promise((resolve, reject) => {
ipfs.getFile(this.get('ipfsHash')).then(content => {
let contributionJSON = JSON.parse(content);
let contribution = Ember.Object.create(contributionJSON);
this.setProperties({
kind: contribution.get('kind'),
description: contribution.get('description'),
url: contribution.get('url')
});
// TODO load details
// let details = profile.get('accounts');
Ember.Logger.debug('[proposal] loaded contribution details', contributionJSON);
resolve();
}).catch((err) => {
Ember.Logger.error('[proposal] error trying to load contribution details', this.get('ipfsHash'), err);
reject(err);
});
});
return promise;
},
/**
* Creates a JSON-LD object of the contribution, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
*
* @method
* @public
*/
contributionToJSON() {
if (isEmpty(this.get('recipientProfile'))) {
throw new Error('IPFS hash for recipient profile missing from proposal object');
}
if (isEmpty(this.get('kind')) || isEmpty(this.get('description'))) {
throw new Error('Missing one or more required properties: kind, description');
}
let contribution = {
"@context": "https://schema.kosmos.org",
"@type": "Contribution",
"contributor": {
"ipfs": this.get('recipientProfile')
},
"kind": this.get('kind'),
"description": this.get('description'),
"details": {}
};
if (isPresent(this.get('url'))) {
contribution["url"] = this.get('url');
}
return contribution;
},
/**
* Returns the JSON-LD representation of the contribution as a string
*
* @method
* @public
*/
serializeContribution() {
return JSON.stringify(this.contributionToJSON());
}
}); });
+3 -2
View File
@@ -1,12 +1,13 @@
import EmberRouter from '@ember/routing/router'; import Ember from 'ember';
import config from './config/environment'; import config from './config/environment';
const Router = EmberRouter.extend({ const Router = Ember.Router.extend({
location: config.locationType, location: config.locationType,
rootURL: config.rootURL rootURL: config.rootURL
}); });
Router.map(function() { Router.map(function() {
this.route('spinner');
this.route('proposals', function() { this.route('proposals', function() {
this.route('new'); this.route('new');
}); });
+2 -25
View File
@@ -1,27 +1,4 @@
import { inject as injectService } from '@ember/service'; import Ember from 'ember';
import Route from '@ember/routing/route';
export default Route.extend({ export default Ember.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();
});
}
}); });
+34
View File
@@ -0,0 +1,34 @@
import Ember from 'ember';
import Contributor from 'kredits-web/models/contributor';
export default Ember.Route.extend({
kredits: Ember.inject.service(),
beforeModel(transition) {
const kredits = this.get('kredits');
if (kredits.get('web3') && kredits.get('web3Provided')) {
kredits.get('web3').eth.getAccounts((error, accounts) => {
if (error || accounts.length === 0) {
if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) {
transition.retry();
}
}
});
}
},
model() {
let kredits = this.get('kredits');
return Ember.RSVP.hash({
contributors: kredits.getContributors(),
totalSupply: kredits.getValueFromContract('tokenContract', 'totalSupply'),
proposals: kredits.getProposals(),
contributions: kredits.getContributions(),
newContributor: Contributor.create({ kind: 'person' })
});
}
});
+37
View File
@@ -0,0 +1,37 @@
import Ember from 'ember';
import Proposal from 'kredits-web/models/proposal';
const {
Route,
RSVP,
inject: {
service
}
} = Ember;
export default Route.extend({
kredits: service(),
model(params) {
const proposal = Proposal.create({
recipientAddress: params.recipient,
amount: params.amount,
url: params.url,
kind: params.kind || 'dev',
ipfsHash: params.ipfsHash
});
return RSVP.hash({
proposal,
contributors: this.get('kredits').getContributors()
});
},
setupController(controller, model) {
this._super(controller, model.proposal);
controller.set('contributors', model.contributors);
}
});
+4
View File
@@ -0,0 +1,4 @@
import Ember from 'ember';
export default Ember.Route.extend({
});
+35
View File
@@ -0,0 +1,35 @@
import Ember from 'ember';
import ipfsAPI from 'npm:ipfs-api';
import config from 'kredits-web/config/environment';
export default Ember.Service.extend({
ipfsInstance: null,
ipfs: function() {
if (this.get('ipfsInstance')) {
return this.get('ipfsInstance');
}
let ipfs = ipfsAPI(config.ipfs);
this.set('ipfsInstance', ipfs);
return ipfs;
}.property('ipfsInstance'),
storeFile(content) {
let ipfs = this.get('ipfs');
return ipfs.add(new ipfs.Buffer(content)).then(res => {
Ember.Logger.debug('[ipfs] stored content in IPFS', content, res[0].hash);
return res[0].hash;
});
},
getFile(hash) {
return this.get('ipfs').cat(hash, { buffer: true }).then(res => {
return res.toString();
}, err => {
Ember.Logger.error('[ipfs] error trying to fetch file', hash, err);
throw err;
});
}
});
+269 -216
View File
@@ -1,231 +1,284 @@
import ethers from 'npm:ethers'; import Ember from 'ember';
import Kredits from 'npm:kredits-contracts'; import Web3 from 'npm:web3';
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';
import Contribution from 'kredits-web/models/contribution';
import kreditsContracts from 'npm:kredits-contracts';
import uuid from 'npm:uuid';
const {
Service,
isPresent,
inject: {
service
}
} = Ember;
export default Service.extend({ export default Service.extend({
currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded. ipfs: service(),
currentUser: null,
currentUserIsContributor: notEmpty('currentUser'),
currentUserIsCore: alias('currentUser.isCore'),
hasAccounts: notEmpty('currentUserAccounts'),
accountNeedsUnlock: computed('currentUserAccounts', function() {
return this.currentUserAccounts && isEmpty(this.currentUserAccounts);
}),
// this is called in the routes beforeModel(). So it is initialized before everything else web3Instance: null,
// and we can rely on the ethProvider and the potential currentUserAccounts to be available web3Provided: false, // Web3 provided (using Mist Browser, Metamask et al.)
getEthProvider: function() {
return new RSVP.Promise((resolve) => { web3: function() {
let ethProvider; if (this.get('web3Instance')) {
let networkId; return this.get('web3Instance');
if (typeof window.web3 !== 'undefined') { }
console.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
networkId = parseInt(window.web3.version.network); let web3Instance;
ethProvider = new ethers.providers.Web3Provider(
window.web3.currentProvider, if (typeof window.web3 !== 'undefined') {
{ chainId: networkId } Ember.Logger.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
); web3Instance = window.web3;
ethProvider.listAccounts().then((accounts) => { this.set('web3Provided', true);
this.set('currentUserAccounts', accounts); } else {
const ethSigner = accounts.length === 0 ? null : ethProvider.getSigner(); Ember.Logger.debug('[kredits] Creating new instance from npm module class');
resolve({ let providerUrl = localStorage.getItem('config:web3ProviderUrl') || config.web3ProviderUrl;
ethProvider, let provider = new Web3.providers.HttpProvider(providerUrl);
ethSigner web3Instance = new Web3(provider);
}
this.set('web3Instance', web3Instance);
window.web3 = web3Instance;
return web3Instance;
}.property('web3Instance'),
currentUserAccounts: function() {
return (this.get('web3Provided') && this.get('web3').eth.accounts) || [];
}.property('web3Provided', 'web3'),
initializeKreditsContract() {
let contract = null;
if (isPresent(config.contractMetadata)) {
if (localStorage.getItem('config:networkId')) {
config.contractMetadata['networkId'] = localStorage.getItem('config:networkId');
}
contract = kreditsContracts(this.get('web3'), config.contractMetadata)['Kredits'];
} else {
contract = kreditsContracts(this.get('web3'))['Kredits'];
}
return contract;
},
kreditsContract: function() {
if (this.get('kreditsContractInstance')) {
return this.get('kreditsContractInstance');
}
let contract = this.initializeKreditsContract();
this.set('kreditsContractInstance', contract);
// window.Kredits = contract;
return contract;
}.property('kreditsContractInstance', 'web3'),
tokenContract: function() {
if (this.get('tokenContractInstance')) {
return this.get('tokenContractInstance');
}
let contract = kreditsContracts(this.get('web3'), config.contractMetadata)['Token'];
this.set('tokenContractInstance', contract);
window.Token = contract;
return contract;
}.property('tokenContractInstance', 'web3'),
getValueFromContract(contract, contractMethod, ...args) {
Ember.Logger.debug('[kredits] read from contract', contract);
return new Ember.RSVP.Promise((resolve, reject) => {
this.get(contract)[contractMethod](...args, (err, data) => {
if (err) { reject(err); return; }
resolve(data);
});
});
},
getContributorData(i) {
let promise = new Ember.RSVP.Promise((resolve, reject) => {
this.getValueFromContract('kreditsContract', 'contributorAddresses', i).then(address => {
this.getValueFromContract('kreditsContract', 'contributors', address).then(person => {
this.getValueFromContract('tokenContract', 'balanceOf', address).then(balance => {
Ember.Logger.debug('[kredits] person', address, person);
let contributor = Contributor.create({
address: address,
ipfsHash: person[2],
kredits: balance.toNumber(),
isCurrentUser: this.get('currentUserAccounts').includes(address)
});
contributor.loadProfile(this.get('ipfs')).then(
() => resolve(contributor),
err => reject(err)
);
}); });
}); });
} else { }).catch(err => reject(err));
console.debug('[kredits] Creating new instance from npm module class'); });
networkId = parseInt(config.contractMetadata.networkId); return promise;
console.debug(`[kredits] networkId=${networkId} providerURL: ${config.web3ProviderUrl}`); },
ethProvider = new ethers.providers.JsonRpcProvider(
config.web3ProviderUrl, getContributors() {
{ chainId: networkId } return this.getValueFromContract('kreditsContract', 'contributorsCount').then(contributorsCount => {
); let contributors = [];
resolve({
ethProvider: ethProvider, for(var i = 0; i < contributorsCount.toNumber(); i++) {
ethSigner: null contributors.push(this.getContributorData(i));
}
return Ember.RSVP.all(contributors);
});
},
getProposalData(i) {
let promise = new Ember.RSVP.Promise((resolve, reject) => {
this.getValueFromContract('kreditsContract', 'proposals', i).then(p => {
let proposal = Proposal.create({
id : i,
creatorAddress : p[0],
recipientAddress : p[1],
votesCount : p[2].toNumber(),
votesNeeded : p[3].toNumber(),
amount : p[4].toNumber(),
executed : p[5],
url : p[6],
ipfsHash : p[7]
}); });
if (proposal.get('ipfsHash')) {
proposal.loadContribution(this.get('ipfs')).then(
() => resolve(proposal),
err => reject(err)
);
} else {
Ember.Logger.warn('[kredits] proposal from blockchain is missing IPFS hash', proposal);
resolve(proposal);
}
}).catch(err => reject(err));
});
return promise;
},
getProposals() {
return this.getValueFromContract('kreditsContract', 'proposalsCount').then(proposalsCount => {
let proposals = [];
for(var i = 0; i < proposalsCount.toNumber(); i++) {
proposals.push(this.getProposalData(i));
}
return Ember.RSVP.all(proposals);
});
},
vote(proposalId) {
Ember.Logger.debug('[kredits] vote for', proposalId);
return new Ember.RSVP.Promise((resolve, reject) => {
this.get('kreditsContract').vote(proposalId, (err, data) => {
if (err) { reject(err); return; }
Ember.Logger.debug('[kredits] vote response', data);
resolve(data);
});
});
},
addContributor(contributor) {
Ember.Logger.debug('[kredits] add contributor', contributor);
contributor.setProperties({
kredits: 0,
isCurrentUser: this.get('currentUserAccounts').includes(contributor.address)
});
let id = uuid.v4();
return new Ember.RSVP.Promise((resolve, reject) => {
this.get('ipfs').storeFile(contributor.serialize()).then(ipfsHash => {
contributor.set('ipfsHash', ipfsHash);
this.get('kreditsContract').addContributor(contributor.address, contributor.name, contributor.ipfsHash, contributor.isCore, id, (err, data) => {
if (err) { reject(err); return; }
Ember.Logger.debug('[kredits] add contributor response', data);
resolve(contributor);
});
});
});
},
addProposal(proposal) {
return new Ember.RSVP.Promise((resolve, reject) => {
const {
recipientAddress,
amount,
url
} = proposal.getProperties('recipientAddress', 'amount', 'url');
this.get('ipfs').storeFile(proposal.serializeContribution()).then(ipfsHash => {
this.get('kreditsContract').addProposal(recipientAddress, amount, url, ipfsHash, (err, data) => {
if (err) { reject(err); return; }
Ember.Logger.debug('[kredits] add proposal response', data);
resolve();
});
});
});
},
getTokenMintedEvents(fromBlock=0, toBlock='latest') {
return new Ember.RSVP.Promise((resolve, reject) => {
const minted = this.get('tokenContract')
.Minted({}, {fromBlock: fromBlock, toBlock: toBlock});
minted.get((err, res) => {
if (err) {
Ember.Logger.error('[kredits] Error reading Token#Minted events', res);
reject(err);
return;
}
Ember.Logger.warn('[kredits] Token#Minted events', res);
resolve(res);
});
});
},
getContributions() {
// this.getTokenMintedEvents().then(events => {
let contributions = [];
config.fixtures.mintedEvents.forEach(mintedEvent => {
contributions.push(this.getContributionData(mintedEvent));
});
return Ember.RSVP.all(contributions);
// });
},
getContributionData(mintedEvent) {
return new Ember.RSVP.Promise((resolve, reject) => {
let contribution = Contribution.create({
recipientAddress: mintedEvent.args.recipientAddress,
contributorId: mintedEvent.args.contributorId,
amount: mintedEvent.args.amount,
ipfsHash: mintedEvent.args.reference
});
if (contribution.get('ipfsHash')) {
contribution.loadDetails(this.get('ipfs')).then(
() => resolve(contribution),
err => reject(err)
);
} else {
Ember.Logger.warn('[kredits] contribution from Minted event is missing IPFS hash', contribution);
resolve(contribution);
} }
}); });
}, },
setup() { // logKreditsContract: function() {
return this.getEthProvider().then((providerAndSigner) => { // Ember.Logger.debug('[kredits] kreditsContract', this.get('kreditsContract'));
// }.on('init')
let kredits = new Kredits(providerAndSigner.ethProvider, providerAndSigner.ethSigner, {
ipfsConfig: config.ipfs
});
return kredits
.init()
.then((kredits) => {
this.set('kredits', kredits);
if (this.currentUserAccounts && this.currentUserAccounts.length > 0) {
this.getCurrentUser.then((contributorData) => {
this.set('currentUser', contributorData);
});
}
return kredits;
});
});
},
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() {
return this.kredits.Contributor.all()
.then((contributors) => {
return contributors.map((contributor) => {
return Contributor.create(contributor);
});
});
},
addProposal(attributes) {
console.debug('[kredits] add proposal', attributes);
return this.kredits.Operator.addProposal(attributes)
.then((data) => {
console.debug('[kredits] add proposal response', data);
attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
return Proposal.create(attributes);
});
},
getProposals() {
return this.kredits.Operator.all()
.then((proposals) => {
return proposals.map((proposal) => {
proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString());
return Proposal.create(proposal);
});
});
},
vote(proposalId) {
console.debug('[kredits] vote for', proposalId);
return this.kredits.Operator.functions.vote(proposalId)
.then((data) => {
console.debug('[kredits] vote response', data);
return data;
});
},
getCurrentUser: computed('kredits.provider', function() {
if (isEmpty(this.currentUserAccounts)) {
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);
},
}); });
+1 -14
View File
@@ -5,17 +5,4 @@ $green: #97fb68;
$yellow: #fbe468; $yellow: #fbe468;
$red: #fb6868; $red: #fb6868;
$primary-color: $blue; $primaryColor: $blue;
$body-text-color: #fff;
@mixin body-background {
background-image: linear-gradient(to bottom, rgba(22, 21, 40, .4), rgba(0, 0, 0, .75)), url('/img/bg.jpg');
background-repeat: none;
background-attachment: fixed;
}
#topbar {
background-color: rgba(255, 0, 255, .2);
}
+14 -36
View File
@@ -1,53 +1,31 @@
#topbar { section {
height: 3rem; @include outer-container;
} margin-bottom: 8rem;
main {
padding: 1rem;
&#index {
width: 100%;
display: grid;
grid-row-gap: 2rem;
grid-template-areas:
"stats"
"contributions";
}
}
@media (min-width: 550px) {
main {
&#index {
grid-column-gap: 4rem;
grid-row-gap: 2rem;
grid-template-columns: 2fr 4fr;
grid-template-areas:
"stats contributions";
}
}
}
main section {
margin-bottom: 5rem;
&:first-of-type { &:first-of-type {
margin-top: 2rem; margin-top: 6rem;
} }
@include media($mobile) { @include media($mobile) {
margin-bottom: 5rem; margin-bottom: 5rem;
&:first-of-type {
&#proposals-open, &#proposals-closed { margin-top: 2rem;
margin-top: 0;
} }
} }
header { header {
@include span-columns(12);
padding-bottom: 3rem; padding-bottom: 3rem;
text-align: center; text-align: center;
// background-color: purple;
@include media($mobile) { @include media($mobile) {
@include span-columns(10);
@include shift(1);
padding-bottom: 2rem; padding-bottom: 2rem;
} }
div.content {
@include span-columns(12);
}
} }
} }
+17 -32
View File
@@ -2,7 +2,6 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
font-size: 14px;
} }
*:hover, *:active, *:focus { *:hover, *:active, *:focus {
@@ -11,9 +10,9 @@
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
@import "bourbon/bourbon"; @import "bourbon";
@import "settings/neat"; @import "settings/neat";
@import "neat/neat"; @import "neat";
@import "settings/breakpoints"; @import "settings/breakpoints";
@import "layout"; @import "layout";
@import "colors"; @import "colors";
@@ -21,10 +20,13 @@
$font-family-sans: 'Open Sans', sans-serif; $font-family-sans: 'Open Sans', sans-serif;
body { body {
@include body-background; background-image: url('/img/bg.jpg');
background-repeat: none;
background-attachment: fixed;
font-family: $font-family-sans; font-family: $font-family-sans;
font-size: 14px;
font-weight: 300; font-weight: 300;
color: $body-text-color; color: #fff;
} }
@media screen and (min-width: 900px) { @media screen and (min-width: 900px) {
@@ -41,11 +43,14 @@ h1, h2, h3, h4, h5, input, button {
section { section {
h2 { h2 {
font-size: 1.4rem; font-size: 2.8rem;
color: $primary-color; color: $primaryColor;
@include media($mobile) {
font-size: 2rem;
}
} }
&#people { &#contributors {
.content { .content {
p.stats { p.stats {
padding-top: 3rem; padding-top: 3rem;
@@ -62,30 +67,18 @@ section {
} }
} }
&#contributions-by-type {
.chart {
width: 50%;
margin-left: auto;
margin-right: auto;
}
@include media($mobile) {
width: 90%;
}
}
&#proposals-open, &#proposals-closed { &#proposals-open, &#proposals-closed {
.actions { .actions {
padding-top: 3rem; padding-top: 3rem;
font-size: 1rem; font-size: 1rem;
color: $primary-color; color: $primaryColor;
text-align: center; text-align: center;
@include media($mobile) { @include media($mobile) {
padding-top: 2rem; padding-top: 2rem;
} }
a { a {
color: $primary-color; color: $primaryColor;
} }
} }
} }
@@ -95,7 +88,7 @@ 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: $primary-color; color: $primaryColor;
border-radius: 3px; border-radius: 3px;
font-weight: 500; font-weight: 500;
text-transform: uppercase; text-transform: uppercase;
@@ -107,16 +100,8 @@ button, input[type=submit] {
} }
} }
@media (min-width: 550px) {
section {
h2 {
font-size: 2rem;
}
}
}
@import "components/topbar";
@import "components/loading-spinner"; @import "components/loading-spinner";
@import "components/contributor-list"; @import "components/contributor-list";
@import "components/add-contributor"; @import "components/add-contributor";
@import "components/proposal-list"; @import "components/proposal-list";
@import "components/contribution-list";
-19
View File
@@ -1,19 +0,0 @@
@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
@@ -1,425 +0,0 @@
// 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
@@ -1,90 +0,0 @@
// 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
@@ -1,26 +0,0 @@
@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
@@ -1,48 +0,0 @@
@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
@@ -1,25 +0,0 @@
@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
@@ -1,25 +0,0 @@
@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
@@ -1,64 +0,0 @@
@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
@@ -1,25 +0,0 @@
@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
@@ -1,30 +0,0 @@
@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
@@ -1,31 +0,0 @@
@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
@@ -1,27 +0,0 @@
/// 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
@@ -1,26 +0,0 @@
@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
@@ -1,26 +0,0 @@
@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
@@ -1,48 +0,0 @@
@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
@@ -1,66 +0,0 @@
@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
@@ -1,27 +0,0 @@
@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
@@ -1,51 +0,0 @@
@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
@@ -1,113 +0,0 @@
@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
@@ -1,34 +0,0 @@
@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
@@ -1,63 +0,0 @@
@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
@@ -1,29 +0,0 @@
@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
@@ -1,61 +0,0 @@
// 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
@@ -1,5 +0,0 @@
@mixin appearance($value) {
@include _bourbon-deprecate-for-prefixing("appearance");
@include prefixer(appearance, $value, webkit moz ms o spec);
}
-5
View File
@@ -1,5 +0,0 @@
@mixin backface-visibility($visibility) {
@include _bourbon-deprecate-for-prefixing("backface-visibility");
@include prefixer(backface-visibility, $visibility, webkit spec);
}
-44
View File
@@ -1,44 +0,0 @@
//************************************************************************//
// 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
@@ -1,57 +0,0 @@
//************************************************************************//
// 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
@@ -1,61 +0,0 @@
@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
@@ -1,6 +0,0 @@
@mixin calc($property, $value) {
@include _bourbon-deprecate-for-prefixing("calc");
#{$property}: -webkit-calc(#{$value});
#{$property}: calc(#{$value});
}
-67
View File
@@ -1,67 +0,0 @@
@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
@@ -1,6 +0,0 @@
@mixin filter($function: none) {
@include _bourbon-deprecate-for-prefixing("filter");
// <filter-function> [<filter-function]* | none
@include prefixer(filter, $function, webkit spec);
}
-327
View File
@@ -1,327 +0,0 @@
// 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
@@ -1,24 +0,0 @@
@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
@@ -1,6 +0,0 @@
@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
@@ -1,12 +0,0 @@
// 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
@@ -1,6 +0,0 @@
@mixin hyphens($hyphenation: none) {
@include _bourbon-deprecate-for-prefixing("hyphens");
// none | manual | auto
@include prefixer(hyphens, $hyphenation, webkit moz ms spec);
}
-15
View File
@@ -1,15 +0,0 @@
@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
@@ -1,38 +0,0 @@
// 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
@@ -1,40 +0,0 @@
@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
@@ -1,12 +0,0 @@
@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
@@ -1,10 +0,0 @@
@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
@@ -1,40 +0,0 @@
// 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
@@ -1,44 +0,0 @@
@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
@@ -1,27 +0,0 @@
@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
@@ -1,21 +0,0 @@
@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
@@ -1,81 +0,0 @@
// 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
@@ -1,5 +0,0 @@
@mixin user-select($value: none) {
@include _bourbon-deprecate-for-prefixing("user-select");
@include prefixer(user-select, $value, webkit moz ms spec);
}
-16
View File
@@ -1,16 +0,0 @@
@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
@@ -1,25 +0,0 @@
@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
@@ -1,31 +0,0 @@
@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
@@ -1,16 +0,0 @@
@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
@@ -1,26 +0,0 @@
@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
@@ -1,16 +0,0 @@
@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
@@ -1,18 +0,0 @@
@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
@@ -1,69 +0,0 @@
// 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
@@ -1,18 +0,0 @@
// 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
@@ -1,20 +0,0 @@
// 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
@@ -1,24 +0,0 @@
@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
@@ -1,22 +0,0 @@
@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
@@ -1,24 +0,0 @@
@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);
}
@@ -1,32 +0,0 @@
// 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;
}
}

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