...
 
Commits (388)
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
node_modules/
qowala_app/config/twitter.json
.vagrant
*.sublime-project
*.sublime-workspace
*.vim
*.swp
language: node_js
node_js:
- "0.10"
before_script:
- cd qowala_app
- npm install
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
# Qowala configuration
- Copy and rename **config/template_twitter.json** to **config/twitter.json** and put in your Twitter credentials (you can get the credentials on *Twitter Developer Website* at http://apps.twitter.com after creating your own app).
- Give your application Read & Write permissions on Twitter Application Management Dashboard
![Twitter App Manager](http://www.killiankemps.fr/data/images/qowala-live-twitter-settings.png)
- For the callback URL in **config/twitter.json** you have to put the domain name where the app will be hosted and add to it '/auth/twitter/callback'.
# Qowala Development mode
Once the Qowala configuration done as written above, the recommended way to launch the app for development is to use **Vagrant**. No need to install Qowala Vagrant will do it for you.
In the Qowala's directory do
vagrant up
Then access to the VM
vagrant ssh
Move the directory containing your files
cd /vagrant/
Start the server
nodejs server.js
You will then be able to access the client in your browser at **http://192.168.12.34:8080/** (The IP address can be changed in the Vagrantfile)
FROM node:0.12
MAINTAINER Killian Kemps
RUN mkdir /var/app
COPY qowala_app/package.json var/app/package.json
RUN cd /var/app && npm install
RUN npm install -g forever
COPY qowala_app var/app/
WORKDIR /var/app
EXPOSE 8080
ADD docker/service-start.sh /
CMD ["/service-start.sh"]
# How to install Qowala
## Qowala configuration
- Copy and rename **config/template_twitter.json** to **config/twitter.json** and put in your Twitter credentials (you can get the credentials on *Twitter Developer Website* at http://apps.twitter.com after creating your own app).
- Give your application Read & Write permissions on Twitter Application Management Dashboard
![Twitter App Manager](http://www.killiankemps.fr/data/images/qowala-live-twitter-settings.png)
- For the callback URL in **config/twitter.json** you have to put the domain name where the app will be hosted and add to it '/auth/twitter/callback'.
## Qowala easy installation
Just launch the **Qowala_InstallationScript.sh**, it will do automatically what is below
## Qowala manual installation
### Ubuntu 14.04 process:
#### Install NodeJS
curl -sL https://deb.nodesource.com/setup | sudo bash -
sudo apt-get install nodejs
sudo apt-get install build-essential
#### Install MongoDB
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
sudo apt-get update
sudo apt-get install -y mongodb-org
#### Install the dependencies
npm install
*NodeJS installation process inspired by Digital Ocean: https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-an-ubuntu-14-04-server*
*MongoDB installation process inspired by MongoDB.org http://docs.mongodb.org/manual/tutorial/install-mongodb-on-ubuntu/*
## Qowala Simple Run
nodejs server.js
## Qowala Production mode
sudo npm install -g forever
forever -l forever.log -ao stdout.log -e stderr.log start server.js
This diff is collapsed.
#!/bin/bash
# The MIT License (MIT)
#
# Copyright (c) 2015 Killian Kemps
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
###############################
#
# This script was meant to be used with Ubuntu 14.04 Trusty
# This script will install what is required for Qowala live then install the Twitter app
#
# The components installation process was being inspired by Digital Ocean tutorial accessible at : https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-an-ubuntu-14-04-server
#
################################
# Get the servers IP adress
ipadress=$(ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//' | head -n 1)
echo "*******************************************************************************"
echo "*** Qowala Installation Script ***"
echo "*******************************************************************************"
echo "This script will try to install MongoDB, NodeJS and its modules."
echo "It will then download and install Qowala so you will be able to access it at (you will set the domain name during the installation) :
"
echo ' http://'$ipadress'/:8080' '
'
read -p 'Do you want to launch the installation script ? [Y/n] : ' ifInstall
if [ $ifInstall = "Y" ] || [ $ifInstall = 'y' ]
then
sudo apt-get update
# Install NodeJS
curl -sL https://deb.nodesource.com/setup | sudo bash -
sudo apt-get install -y nodejs
sudo apt-get install -y build-essential
# Install MongoDB
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
sudo apt-get update
sudo apt-get install -y mongodb-org
sudo npm install
# Install forever for reliability of the server
sudo npm install -g forever
echo "*******************************************************************************"
echo "*** Qowala Installation Script ***"
echo "*******************************************************************************"
echo "We will now configure your Twitter app.
"
echo "1) If you haven't done it before, first create a Twitter account at www.twitter.com"
echo "2) Then, become a Twitter developer by going on dev.twitter.com."
echo "3) Finally, create a Twitter application with the name you want. You should now have your Consumer Token and Consumer Secret.
"
read -p 'What is your Consumer Token ? (Use Ctrl+Shift+V to paste into the terminal, be careful that there is no space before your Token) : ' consumerKey
read -p 'What is your Consumer Secret ? : ' consumerSecret
echo "One last information needed,"
read -p 'What is the domain name of the server ? : ' domainName
# Creating the twitter configuration file
sudo echo '{"consumerKey" : "'$consumerKey'",'' "consumerSecret" : "'$consumerSecret'", "callbackURL": "http://'$domainName':8080/auth/twitter/callback"}' >> config/twitter.json
# Launch the application
sudo forever -l forever.log -ao stdout.log -e stderr.log start app.js
# Display end message
message='Your Qowala installation is ready to be used at : http://'
echo "*******************************************************************************"
echo "*** Qowala Installation Script ***"
echo "*******************************************************************************"
echo ""
echo $message$domainName'/:8080'
echo "If they are errors, check the stderr.log file"
else
echo "Okay, good bye !"
fi
![alt tag](public/img/logo-icn+name-green.png)
[![Build Status](https://travis-ci.org/KillianKemps/Qowala.svg?branch=master)](https://travis-ci.org/KillianKemps/Qowala)
Qowala
======
Open source and growing! We’re aiming to become your favorite tool to survive in the social networks jungle. First is Twitter, with many more to come.
Qowala is an open source Twitter client that wishes to give you control on your usage of social networks.
Created by Killian Kemps (Developer) and joined by Manuel Lemaire (Developer), Adrien Touminet (Designer) and Antoine Beauvillain (Designer).
### Official instance:
The official instance server is at : https://live.qowala.org
![Qowala Screenshot](screenshot.png)
### Git repositories:
They are two mirrored git repositories of the project:
- Framasoft: https://git.framasoft.org/KillianKemps/Qowala
- Github: https://github.com/KillianKemps/Qowala
### Project management:
We are using the open source Taiga project management tool. You can access it at https://tree.taiga.io/project/qowala-live/backlog to follow the development process and interact with us for issues, requests for enhancement, etc.
### Qowala v1.0 functionalities:
- One pod for several users
- Get live notifications when users interact with you
- Display user's timeline
- Display user's lists
- Track tweets by hashtags
- Send tweets
- Retweet tweets
- Reply to tweets
# Qowala's installation
Follow the installation process [here](INSTALLATION.md)
# Qowala for developers
Want to contribute? Welcome! Instructions are [here](DEVELOPERS.md)
# Qowala's License
Please check the license [here](LICENSE.md)
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.network "private_network", ip: "192.168.12.34"
config.vm.provision "shell", path: "vagrant-installation.sh"
# config.vm.provision "shell", inline: "nodejs /vagrant/server.js",
# run: "always"
end
# Changelog
## 1.0.0 - xxxx/xx/xx
* Reskin all Qowala's interface but keep existing functions.
* Add emojis support (but only partial)
## 1.0.0.beta-4 - 2016/01/04
* Add user profile modal with links to its tweets, mentions, lists, favorites, and buttons to tweet and follow/unfollow.
* Add username auto-completion when writing a tweet.
## 1.0.0.beta-3 - 2015/09/07
* Add notifications support when user is connected.
The panel can be opened with a button which is showing
latests alerts.
These following events are now supported:
* Mentions
* Replies
* Favorites
* Retweets
* Follows
* Lists addition
* Add popup at start for deprecated web browsers.
This popup is advising to switch to the latest version of
Firefox, Chrome or Opera.
* Add reply button on tweets.
It is now possible to reply to a tweet and to have the input
field prepared with the receiver username.
qowala_app:
build: .
ports:
- "8080:8080"
links:
- mongo
mongo:
image: mongo
ports:
- "27017:27017"
#!/bin/bash
# WORKDIR
cd /var/app
# Start NodeJS server
forever -l forever.log -ao stdout.log -e stderr.log server.js
// Karma configuration
// Generated on Thu May 14 2015 16:09:59 GMT+0000 (UTC)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
// "node_modules/chai/chai.js",
// "../../node_modules/karma-jasmine/",
'public/js/tests/*.js',
'public/js/*.js'
],
// list of files to exclude
exclude: [
'public/js/main.js'
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: [],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};
module.exports = {
url : 'mongodb://localhost/qowala-live'
}
\ No newline at end of file
{
"consumerKey" : "",
"consumerSecret" : "",
"accessToken" : "",
"accessTokenSecret" : "",
"callbackURL" : ""
}
\ No newline at end of file
var users = require('../lib/users');
exports.getDashboard = function(req, res) {
var userId;
if (req.user){
userId = req.user;
users.getUserName(userId, function(username){
res.render('home/dashboard', {
userId : userId,
username: username,
piwikUrl: process.env.QOWALA_PIWIK_URL,
piwikId: process.env.QOWALA_PIWIK_ID
});
});
}
else{
res.redirect('/');
}
console.log(req.session);
};
exports.getLogin = function(req, res) {
if (req.user) {
res.redirect('/dashboard');
}
else {
res.render('home/login', {
piwikUrl: process.env.QOWALA_PIWIK_URL,
piwikId: process.env.QOWALA_PIWIK_ID
});
}
};
FROM node:0.12
MAINTAINER Killian Kemps
#RUN apt-get update
#RUN apt-get install -y curl
# Install nvm for NodeJS
#RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.4/install.sh | bash
#RUN export NVM_DIR="/root/.nvm"
#RUN [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
#
## Install NodeJS v0.12 via nvm
#RUN nvm install v0.12
#RUN nvm use v0.12
RUN mkdir /var/app
# Fails here as it only copies the Dockerfile
COPY * /var/app/
WORKDIR /var/app/
RUN cd /var/app/ && ls
RUN npm install
RUN npm install -g forever
EXPOSE 8080
CMD ["forever", "-l forever.log -ao stdout.log -e stderr.log start server.js"]
FROM ubuntu
MAINTAINER Killian Kemps
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 || true
RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
RUN apt-get update && apt-get install -y \
build-essential \
mongodb-org
EXPOSE 27017
CMD ["mongod"]
var passport = require('passport')
, TwitterStrategy = require('passport-twitter').Strategy;
try {
var configTwitter = require('../config/twitter');
} catch (error) {
console.log('Warning: There is no configuration file for twitter!');
}
var users = require('./users');
function passportAuthentication (app){
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
done(null, id);
// User.findById(id, function (err, user) {
// done(err, user);
// });
});
passport.use(new TwitterStrategy({
consumerKey: configTwitter ? configTwitter.consumerKey : process.env.QOWALA_TWITTER_KEY,
consumerSecret: configTwitter ? configTwitter.consumerSecret : process.env.QOWALA_TWITTER_SECRET,
callbackURL: configTwitter ? configTwitter.callbackURL : process.env.QOWALA_TWITTER_CALLBACK
},
function(token, tokenSecret, profile, done) {
users.setInformations(profile.id, token, tokenSecret, profile.displayName, profile.username, profile._json.profile_image_url, function(){
done(null, profile);
});
}
));
}
module.exports = passportAuthentication;
var users = require('./users');
module.exports = function(io) {
var numberConnectedUsers = 0;
io.sockets.on('connection', function(socket){
console.log('Connected');
var localUserId = null;
socket.on('auth', function(userId){
numberConnectedUsers++;
localUserId = userId;
console.log('got the auth: ' + localUserId);
console.log('There are currenly ', numberConnectedUsers, ' users connected');
users.setSocket(localUserId, socket);
users.getColumnsLayout(localUserId, function(columnsLayout){
socket.emit('columnsLayout', columnsLayout);
});
users.getUserLists(localUserId, function(){
users.getListsTweetsCache(localUserId, function(listsTweetsCache){
console.log('Log: sending cached tweets ');
users.broadcast(localUserId, 'tweet', {tweet:listsTweetsCache,
streamSource:'lists'});
users.startStream(localUserId);
users.startListStream(localUserId, listsTweetsCache);
});
users.getUserHomeTimeline(localUserId, function(){});
});
users.getUserNotifications(localUserId, function(notifications){
socket.emit('notifications history', notifications);
});
// Update user's friends at login
users.getUserFriends(localUserId, function(friends){
});
io.emit('numberConnectedUsers', numberConnectedUsers);
});
socket.on('followUser', function(otherUserId){
users.followUser(localUserId, otherUserId);
});
socket.on('unfollowUser', function(otherUserId){
users.unfollowUser(localUserId, otherUserId);
});
socket.on('getFollowedBy', function(otherUserId){
users.getFollowedBy(localUserId, otherUserId, function(followers){
socket.emit('returnFollowedBy', followers);
});
});
socket.on('update lists request', function(lists){
users.setEnabledLists(localUserId, lists, function(){
users.startListStream(localUserId);
});
console.log('Received update lists request : ', lists);
});
socket.on('get list cache', function(listId){
users.getListsTweetsCache(localUserId, function(listsTweetsCache){
for (var list in listsTweetsCache) {
if(list == listId) {
var cachedList = {};
cachedList[listId] = listsTweetsCache[list];
users.broadcast(localUserId, 'tweet', {
tweet: cachedList,
streamSource: 'lists',
});
}
};
});
});
socket.on('update tags request', function(tagsToRequest){
users.setEnabledTags(localUserId, tagsToRequest, function(){
users.startStream(localUserId);
});
console.log('Received update tags request : ', tagsToRequest);
});
socket.on('update columns layout', function(columnsLayout){
users.setColumnsLayout(localUserId, columnsLayout);
console.log('Received update columns layout : ', columnsLayout);
});
socket.on('retweet', function(tweetId){
console.log('Log: Got retweet request with id ', localUserId);
users.sendRetweet(localUserId, tweetId, function(code, data){
if(code === 200){
// socket.emit('retweetedId', retweetedId);
console.log('Log: Retweet sent with id ', data.id_str, '!');
}
else {
console.log('Log: an error occured while retweeting');
}
});
});
socket.on('delete retweet', function(tweetId){
console.log('Log: Got delete tweet request with id ', localUserId);
users.showTweet(localUserId, tweetId, function(tweet){
if( tweet.current_user_retweet){
var tweetToBeDeleted = tweet.current_user_retweet.id_str;
console.log('tweetToBeDeleted: ', tweetToBeDeleted);
if(tweetToBeDeleted) {
users.deleteTweet(localUserId, tweetToBeDeleted, function(){
});
}
}
});
});
socket.on('sendMessage', function(message){
console.log('Log: Got retweet request with id ', localUserId);
users.sendMessage(localUserId, message, function(){
console.log('Log: Message sent!');
});
});
socket.on('searchTweet', function(keyword){
console.log('Log: Got search request with keyword ', keyword);
users.searchTweet(localUserId, keyword, function(tweets){
users.broadcast(localUserId, 'tweet', {
tweet: tweets,
streamSource:'search-timeline',
keyword: keyword,
});
});
});
socket.on('searchUser', function(keyword){
console.log('Log: Got search request with keyword ', keyword);
users.searchUser(localUserId, keyword, function(matchedFriends){
console.log('gonna search with ', keyword);
if(matchedFriends.length != 0) {
users.broadcast(localUserId, 'search-user', {
users: matchedFriends,
keyword: keyword,
});
}
});
});
socket.on('disconnect', function(){
console.log('Log: The client disconnected with id ', localUserId);
if(localUserId != null){
numberConnectedUsers--;
console.log('There are currenly ', numberConnectedUsers,
' users signed in');
io.emit('numberConnectedUsers', numberConnectedUsers);
users.stopStream(localUserId);
}
});
});
};
var users = require('./../users.js');
var expect = require('chai').expect;
describe("Users", function(){
describe(".addUser()", function(){
it("should create single arrays with default values", function(){
// First try to add a user
var userId = 2;
var result = users.addUser(userId);
expect(result.id).to.equal(2);
expect(result.usersArray).to.have.a.property(2);
// Second try to add another user
var userId = 1;
var secondResult = users.addUser(userId);
expect(secondResult.id).to.equal(1);
expect(secondResult.usersArray).to.have.a.property(1);
// Third add first user again to verify that it overwrites the first action
var userId = 2;
var thirdResult = users.addUser(userId);
// At the end this is expected
var expected = [,
{socket:null, pause:false},
{socket:null, pause:false}
];
expect(thirdResult.id).to.equal(2);
expect(thirdResult.usersArray).to.have.a.property(2);
expect(thirdResult.usersArray).to.deep.equal(expected);
});
});
describe(".setSocket()", function(){
it("should store users socket if he exist", function(){
// First test that the socket is assigned to the user
var userId = 2;
var socket = {data: "dump data"};
result = users.setSocket(userId, socket);
// Second test that the socket throws an error when the user doesn't exist
var userId = 3;
expect(users.setSocket.bind(users.setSocket, userId, socket)).to.throw(Error);
// Third test that the users socket is been overwritten
var userId = 2;
var socket = {data: "Another kind of data"};
secondResult = users.setSocket(userId, socket);
var expected = [,
{socket:null, pause:false},
{socket:{data: "Another kind of data"}, pause:false}
];
expect(secondResult).to.deep.equal(expected);
});
});
describe(".broadcast()", function(){
it("should broadcast the user following the hashtag", function(){
var tweet = {tweet: "dump data"};
var updatedTags = {tweet: "dump data"};
var tagsStats = {tweet: "dump data"};
// First test that function throws an error if user doesn't exist
var userId = 4;
expect(users.broadcast.bind(users.broadcast, userId, tweet, updatedTags, tagsStats)).to.throw(Error, 'User doesn\'t exist. Impossible to broadcast');
// Second test that function throws an error if the user's socket hasn't been set
users.addUser(userId);
expect(users.broadcast.bind(users.broadcast, userId, tweet, updatedTags, tagsStats)).to.throw(Error, 'User\'s socket doesn\'t exist. Impossible to broadcast');
// Third test that the function uses the socket emit function when all is ok
users.setSocket(userId, {
emit: function(tweetString, Object){
throw 'Socket being emitted';
}
});
expect(users.broadcast.bind(users.broadcast, userId, tweet, updatedTags, tagsStats)).to.throw('Socket being emitted');
// Fourth test that the function doesn't fire when the user has put pause
users.togglePause(userId);
expect(users.broadcast.bind(users.broadcast, userId, tweet, updatedTags, tagsStats)).not.to.throw('Socket being emitted');
});
});
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
"name": "Qowala-live",
"version": "1.0.0",
"private": "true",
"main": "server.js",
"engines" : {
"node" : ">=0.10.25 <0.12"
},
"dependencies": {
"body-parser": "^1.9.0",
"connect": "^3.2.0",
"connect-redis": "^2.1.0",
"ejs": "^1.0.0",
"express": "^4.9.5",
"express-ejs-layouts": "^1.1.0",
"express-session": "^1.8.2",
"mongoose": "^3.8.5",
"passport": "~0.2.1",
"passport-twitter": "~1.0.2",
"redis": "^0.12.1",
"socket.io": "^1.3.5",
"twit": "^1.1.20"
},
"devDependencies": {
"chai": "^2.3.0",
"jasmine-core": "^2.3.4",
"karma": "^0.12.31",
"karma-chai": "^0.1.0",
"karma-firefox-launcher": "^0.1.6",
"karma-jasmine": "^0.3.5"
},
"scripts": {
"test": "./node_modules/karma/bin/karma start --browsers Firefox --single-run"
}
}
This diff is collapsed.
This diff is collapsed.
// Animated Icons
// --------------------------
.@{fa-css-prefix}-spin {
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear;
}
.@{fa-css-prefix}-pulse {
-webkit-animation: fa-spin 1s infinite steps(8);
animation: fa-spin 1s infinite steps(8);
}
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
// Bordered & Pulled
// -------------------------
.@{fa-css-prefix}-border {
padding: .2em .25em .15em;
border: solid .08em @fa-border-color;
border-radius: .1em;
}
.@{fa-css-prefix}-pull-left { float: left; }
.@{fa-css-prefix}-pull-right { float: right; }
.@{fa-css-prefix} {
&.@{fa-css-prefix}-pull-left { margin-right: .3em; }
&.@{fa-css-prefix}-pull-right { margin-left: .3em; }
}
/* Deprecated as of 4.4.0 */
.pull-right { float: right; }
.pull-left { float: left; }
.@{fa-css-prefix} {
&.pull-left { margin-right: .3em; }
&.pull-right { margin-left: .3em; }
}
// Base Class Definition
// -------------------------
.@{fa-css-prefix} {
display: inline-block;
font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration
font-size: inherit; // can't have font-size inherit on line above, so need to override
text-rendering: auto; // optimizelegibility throws things off #1094
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
// Fixed Width Icons
// -------------------------
.@{fa-css-prefix}-fw {
width: (18em / 14);
text-align: center;
}
/*!
* Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
@import "variables.less";
@import "mixins.less";
@import "path.less";
@import "core.less";
@import "larger.less";
@import "fixed-width.less";
@import "list.less";
@import "bordered-pulled.less";
@import "animated.less";
@import "rotated-flipped.less";
@import "stacked.less";
@import "icons.less";
This diff is collapsed.
// Icon Sizes
// -------------------------
/* makes the font 33% larger relative to the icon container */
.@{fa-css-prefix}-lg {
font-size: (4em / 3);
line-height: (3em / 4);
vertical-align: -15%;
}
.@{fa-css-prefix}-2x { font-size: 2em; }
.@{fa-css-prefix}-3x { font-size: 3em; }
.@{fa-css-prefix}-4x { font-size: 4em; }
.@{fa-css-prefix}-5x { font-size: 5em; }
// List Icons
// -------------------------
.@{fa-css-prefix}-ul {
padding-left: 0;
margin-left: @fa-li-width;
list-style-type: none;
> li { position: relative; }
}
.@{fa-css-prefix}-li {
position: absolute;
left: -@fa-li-width;
width: @fa-li-width;
top: (2em / 14);
text-align: center;
&.@{fa-css-prefix}-lg {
left: (-@fa-li-width + (4em / 14));
}
}
// Mixins
// --------------------------
.fa-icon() {
display: inline-block;
font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration
font-size: inherit; // can't have font-size inherit on line above, so need to override
text-rendering: auto; // optimizelegibility throws things off #1094
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.fa-icon-rotate(@degrees, @rotation) {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
-webkit-transform: rotate(@degrees);
-ms-transform: rotate(@degrees);
transform: rotate(@degrees);
}
.fa-icon-flip(@horiz, @vert, @rotation) {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1);
-webkit-transform: scale(@horiz, @vert);
-ms-transform: scale(@horiz, @vert);
transform: scale(@horiz, @vert);
}
/* FONT PATH
* -------------------------- */
@font-face {
font-family: 'FontAwesome';
src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}');
src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'),
url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'),
url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
// src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
font-weight: normal;
font-style: normal;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Base Class Definition
// -------------------------
.#{$fa-css-prefix} {
display: inline-block;
font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
font-size: inherit; // can't have font-size inherit on line above, so need to override
text-rendering: auto; // optimizelegibility throws things off #1094
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.