├── .gitattributes ├── .gitignore ├── Dockerfile ├── LICENSE ├── Procfile ├── README.md ├── client ├── .bowerrc ├── .editorconfig ├── .jscsrc ├── .jshintrc ├── Gruntfile.js ├── app │ ├── fonts │ │ ├── icons.eot │ │ ├── icons.svg │ │ ├── icons.ttf │ │ └── icons.woff │ ├── images │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon.png │ │ ├── favicon.ico │ │ ├── pic.jpg │ │ └── placeholder.png │ ├── index.html │ ├── robots.txt │ ├── scripts │ │ ├── app.js │ │ ├── dribbble.js │ │ ├── foursquare.js │ │ ├── github.js │ │ ├── instagram.js │ │ ├── lastfm.js │ │ ├── sample-env.js │ │ ├── stream.js │ │ ├── tumblr.js │ │ ├── twitter.js │ │ └── youtube.js │ ├── styles │ │ ├── less │ │ │ ├── app.less │ │ │ ├── base.less │ │ │ ├── components │ │ │ │ ├── animations.less │ │ │ │ ├── headers.less │ │ │ │ ├── icons.less │ │ │ │ ├── mixins.less │ │ │ │ ├── modals.less │ │ │ │ └── reset.less │ │ │ ├── pages │ │ │ │ ├── dribbble.less │ │ │ │ ├── foursquare.less │ │ │ │ ├── github.less │ │ │ │ ├── instagram.less │ │ │ │ ├── lastfm.less │ │ │ │ ├── stream.less │ │ │ │ ├── tumblr.less │ │ │ │ ├── twitter.less │ │ │ │ └── youtube.less │ │ │ └── variables.less │ │ └── main.css │ └── templates │ │ ├── dribbble │ │ ├── details.html │ │ ├── index.html │ │ └── post.html │ │ ├── foursquare │ │ ├── checkin.html │ │ └── index.html │ │ ├── github │ │ ├── activity.html │ │ └── index.html │ │ ├── instagram │ │ ├── details.html │ │ ├── index.html │ │ └── post.html │ │ ├── lastfm │ │ ├── activity.html │ │ └── index.html │ │ ├── nav.html │ │ ├── stream │ │ └── index.html │ │ ├── tumblr │ │ ├── details.html │ │ ├── index.html │ │ └── post.html │ │ ├── twitter │ │ ├── details.html │ │ ├── index.html │ │ └── post.html │ │ └── youtube │ │ ├── details.html │ │ ├── index.html │ │ └── post.html ├── bower.json ├── package-lock.json └── package.json ├── docs ├── README.md ├── TODO.md ├── docker.md ├── dribbble.md ├── foursquare.md ├── github.md ├── heroku.md ├── images │ ├── dribbble.jpg │ ├── foursquare.jpg │ ├── github.jpg │ ├── instagram.jpg │ ├── lastfm.jpg │ ├── responsive.jpg │ ├── stream.jpg │ ├── tumblr.jpg │ ├── twitter.jpg │ └── youtube.jpg ├── instagram.md ├── lastfm.md ├── running.md ├── start.md ├── tumblr.md ├── twitter.md └── youtube.md ├── package-lock.json ├── package.json └── server ├── .sample-env ├── app.js ├── controllers ├── dribbble.js ├── foursquare.js ├── github.js ├── index.js ├── instagram.js ├── lastfm.js ├── stream.js ├── tumblr.js ├── twitter.js └── youtube.js ├── db.js ├── dist ├── favicon.ico ├── fonts │ ├── icons.eot │ ├── icons.svg │ ├── icons.ttf │ └── icons.woff ├── images │ ├── apple-touch-icon-114x114.09232f58.png │ ├── apple-touch-icon-120x120.8a06d66a.png │ ├── apple-touch-icon-144x144.34c36acc.png │ ├── apple-touch-icon-152x152.151d279b.png │ ├── apple-touch-icon-57x57.d65f8463.png │ ├── apple-touch-icon-60x60.f266145b.png │ ├── apple-touch-icon-72x72.c04e1d7d.png │ ├── apple-touch-icon-76x76.a3921321.png │ ├── apple-touch-icon.d65f8463.png │ ├── pic.365ccac6.jpg │ └── placeholder.78c81860.png ├── index.html ├── robots.txt ├── scripts │ ├── scripts.00ac3313.js │ └── vendor.af1e1da5.js └── styles │ └── main.d73c3cd7.css ├── models ├── dribbble.js ├── foursquare.js ├── github.js ├── instagram.js ├── lastfm.js ├── tumblr.js ├── twitter.js └── youtube.js └── utils └── dates.js /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /client/.tmp 2 | /client/app/scripts/env.js 3 | 4 | *.sublime-project 5 | *.sublime-workspace 6 | 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | 12 | # Local development 13 | *.env 14 | *.dev 15 | .DS_Store 16 | 17 | # Docker 18 | docker-compose.yml 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (http://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules 45 | jspm_packages 46 | bower_components 47 | 48 | # Optional npm cache directory 49 | .npm 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6.9.4 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y --no-install-recommends \ 5 | nodejs 6 | 7 | RUN npm install -g bower grunt-cli 8 | 9 | ADD . / 10 | 11 | # setting up client 12 | WORKDIR /client 13 | RUN bower install --allow-root 14 | RUN npm install; exit 0 15 | RUN grunt clean --force 16 | RUN grunt build --force 17 | 18 | # setting up server 19 | WORKDIR / 20 | RUN npm install 21 | 22 | EXPOSE 3000 23 | WORKDIR /server 24 | 25 | RUN cp -r ../client/app/images ./dist/ 26 | 27 | ENV NODE_ENV="production" 28 | ENTRYPOINT ["node", "app.js"] 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-present, Rodrigo Neri <@rigoneri> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node server/app.js 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### :warning: Unmaintained 2 | 3 | Sorry, but this project is no longer maintained. Please checkout the new version [Syte3](https://github.com/rigoneri/Syte3) if interested. 4 | 5 | -- 6 | 7 | # Syte2 8 | 9 | Syte2 is the new and refined version of [Syte](http://github.com/rigoneri/syte), a personal website with interactive social integrations. Syte2 integrates with Twitter, Instagram, Foursquare, Github, Dribbble, Spotify/Last.fm, YouTube and Tumblr. You can see it in action on [my personal site](http://rigoneri.com). 10 | 11 | ## Stream/Timeline 12 | 13 | Syte2 starts of with your social stream. It groups all integrations into a nice and easy to follow timeline. 14 | 15 | ![Stream](docs/images/stream.jpg) 16 | 17 | ## Social Integrations 18 | 19 | ### Twitter 20 | 21 | Syte2 integrates with Twitter. Every time you tweet it adds that post to your stream. The Twitter page shows a page similar to your profile. 22 | 23 | ![Twitter](docs/images/twitter.jpg) 24 | 25 | ### Github 26 | 27 | Syte2 integrates with Github. Every time you commit something to Github it shows that activity on your stream. The Github page shows your list of public repos and your public activity. 28 | 29 | ![Github](docs/images/github.jpg) 30 | 31 | ### Dribbble 32 | 33 | Syte2 integrates with Dribble. Every time you post a shot to Dribble it shows that shot on your stream. The Dribble page shows a page similar to your Dribbble profile. 34 | 35 | ![Dribbble](docs/images/dribbble.jpg) 36 | 37 | ### Spotify via Last.fm 38 | 39 | Syte2 integrates with Spotify via Last.fm. Every time you listen to a song on Spotify that activity is sent to Last.fm, Syte2 gather and groups that information and adds it to your stream. The Spotify page shows a list of recent tracks you played, as well as your top artists, albums and tracks. 40 | 41 | ![Spotify](docs/images/lastfm.jpg) 42 | 43 | ### Instagram 44 | 45 | Syte2 integrates with Instagram. Every time you post an image to Instagram it shows that image on your stream. The Instagram page shows a page similar to your profile. 46 | 47 | ![Instagram](docs/images/instagram.jpg) 48 | 49 | ### YouTube 50 | 51 | Syte2 integrates with YouTube. Every time you post a video to YouTube it shows that video on your stream. The YouTube page shows a page similar to your YouTube profile. 52 | 53 | ![YouTube](docs/images/youtube.jpg) 54 | 55 | ### Foursquare 56 | 57 | Syte2 integrates with Foursquare. Every time you check-in on Foursquare, after a period of time it shows that check-in on your stream. The Foursquare page shows a list of your recent check-ins and a map with a summary of check-ins in the past month. 58 | 59 | ![Foursquare](docs/images/foursquare.jpg) 60 | 61 | ### Tumblr 62 | 63 | Syte2 integrates with Tumblr. Every time you post something to Tumblr it shows that activity on your stream. The Tumblr page shows all your posts. 64 | 65 | ![Tumblr](docs/images/tumblr.jpg) 66 | 67 | ## Responsive UI 68 | 69 | Syte2 is responsive, it scales to any screen size. 70 | 71 | ![Responsive](docs/images/responsive.jpg) 72 | 73 | ## Technologies Used 74 | 75 | Syte2 is MEAN! It uses [MongoDB](https://www.mongodb.com/), [Express](http://expressjs.com/), [AngularJS](https://angularjs.org/) and [Node.js](https://nodejs.org). 76 | 77 | For development Syte2 uses [LESS](http://lesscss.org) as the CSS pre-processor. 78 | 79 | Syte2 also uses a font for [icons](client/app/fonts). I should probably document how I design and build those fonts. 80 | 81 | For deployment Syte2 uses [Heroku](http://www.heroku.com/) (since they have a free option) with [mLab MongoDB](https://elements.heroku.com/addons/mongolab) add-on. 82 | 83 | ## Setup Instructions 84 | 85 | `NOTE` The setups instructions are mostly for **Mac** not sure if there are any differences for other OSs. 86 | 87 | There are a few steps in order to get Syte2 configured, but don't worry they are pretty easy. 88 | 89 | 1. [Getting started and initial content changes](docs/start.md) 90 | 2. [Running the project locally](docs/running.md) 91 | 3. [Setting up Twitter](docs/twitter.md) 92 | 4. [Setting up Github](docs/github.md) 93 | 5. [Setting up Dribbble](docs/dribbble.md) 94 | 6. [Setting up Spotify & Last.fm](docs/lastfm.md) 95 | 7. [Setting up Instagram](docs/instagram.md) 96 | 8. [Setting up YouTube](docs/youtube.md) 97 | 9. [Setting up Foursquare](docs/foursquare.md) 98 | 10. [Setting up Tumblr](docs/tumblr.md) 99 | 100 | ## Deployment Instructions 101 | 102 | For now I've only deployed my website to Heroku since it's free to start out. I would love to see it deployed to different services. 103 | 104 | * [Heroku deployment instructions](docs/heroku.md) 105 | * [Docker deployment instructions](docks/docker.md) 106 | * AWS deployment instructions (TODO) 107 | * Google Cloud deployment instructions (TODO) 108 | 109 | ## Contributing 110 | 111 | There are plans for several services to be added in the [TODO file](docs/TODO.md). One of these services is a good place to start when looking for ways to help. Also posting/fixing [issues](https://github.com/rigoneri/Syte2/issues) is always helpful. 112 | 113 | ## Credit 114 | 115 | Syte2 was developed by **Rigo** (Rodrigo Neri). 116 | 117 | Check out his personal website at 118 | 119 | Follow him on twitter [@rigoneri](https://twitter.com/rigoneri) 120 | -------------------------------------------------------------------------------- /client/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /client/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /client/.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireCamelCaseOrUpperCaseIdentifiers": true, 3 | "requireCapitalizedConstructors": true, 4 | "requireParenthesesAroundIIFE": true, 5 | "validateQuoteMarks": "'" 6 | } 7 | -------------------------------------------------------------------------------- /client/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "browser": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "esnext": true, 7 | "latedef": true, 8 | "noarg": true, 9 | "node": true, 10 | "strict": true, 11 | "undef": true, 12 | "unused": true, 13 | "globals": { 14 | "angular": false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/app/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/fonts/icons.eot -------------------------------------------------------------------------------- /client/app/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/fonts/icons.ttf -------------------------------------------------------------------------------- /client/app/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/fonts/icons.woff -------------------------------------------------------------------------------- /client/app/images/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /client/app/images/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /client/app/images/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /client/app/images/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /client/app/images/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /client/app/images/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /client/app/images/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /client/app/images/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /client/app/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/apple-touch-icon.png -------------------------------------------------------------------------------- /client/app/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/favicon.ico -------------------------------------------------------------------------------- /client/app/images/pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/pic.jpg -------------------------------------------------------------------------------- /client/app/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/client/app/images/placeholder.png -------------------------------------------------------------------------------- /client/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rigo [Rodrigo Neri] 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /client/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | Disallow: 5 | -------------------------------------------------------------------------------- /client/app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular 4 | .module('clientApp', [ 5 | 'ngAnimate', 6 | 'ngRoute', 7 | 'ngSanitize', 8 | 'angularMoment', 9 | 'angularModalService', 10 | 'angularLazyImg' 11 | ]) 12 | .config(function ($routeProvider) { 13 | $routeProvider 14 | .when('/', { 15 | templateUrl: 'templates/stream/index.html', 16 | controller: 'StreamCtrl' 17 | }) 18 | .when('/twitter', { 19 | templateUrl: 'templates/twitter/index.html', 20 | controller: 'TwitterCtrl' 21 | }) 22 | .when('/dribbble', { 23 | templateUrl: 'templates/dribbble/index.html', 24 | controller: 'DribbbleCtrl' 25 | }) 26 | .when('/instagram', { 27 | templateUrl: 'templates/instagram/index.html', 28 | controller: 'InstagramCtrl' 29 | }) 30 | .when('/youtube', { 31 | templateUrl: 'templates/youtube/index.html', 32 | controller: 'YoutubeCtrl' 33 | }) 34 | .when('/github', { 35 | templateUrl: 'templates/github/index.html', 36 | controller: 'GithubCtrl' 37 | }) 38 | .when('/tumblr', { 39 | templateUrl: 'templates/tumblr/index.html', 40 | controller: 'TumblrCtrl' 41 | }) 42 | .when('/post/:postId', { 43 | templateUrl: 'templates/tumblr/index.html', 44 | controller: 'TumblrViewPostCtrl' 45 | }) 46 | .when('/foursquare', { 47 | templateUrl: 'templates/foursquare/index.html', 48 | controller: 'FoursquareCtrl' 49 | }) 50 | .when('/spotify', { 51 | templateUrl: 'templates/lastfm/index.html', 52 | controller: 'LastfmCtrl' 53 | }) 54 | .otherwise({ 55 | redirectTo: '/' 56 | }); 57 | }) 58 | .run(['$rootScope', '$window', function($rootScope, $window) { 59 | $rootScope.menuOpened = false; 60 | $rootScope.$on('$routeChangeStart', function() { 61 | $rootScope.menuOpened = false; 62 | setTimeout(function() { 63 | $window.scrollTo(0, 0); 64 | }, 500); 65 | }); 66 | }]) 67 | .directive('mainNav', ['$rootScope', function($rootScope) { 68 | return { 69 | restrict: 'E', 70 | templateUrl: 'templates/nav.html', 71 | link: function(scope) { 72 | scope.toggleMenu = function() { 73 | $rootScope.menuOpened = !$rootScope.menuOpened; 74 | }; 75 | } 76 | }; 77 | }]) 78 | .filter('trusted', ['$sce', function ($sce) { 79 | return function(url) { 80 | return $sce.trustAsResourceUrl(url); 81 | }; 82 | }]); 83 | -------------------------------------------------------------------------------- /client/app/scripts/dribbble.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('clientApp') 4 | 5 | .controller('DribbbleCtrl', ['$scope', '$rootScope', '$http', '$window', 'ModalService', 6 | function($scope, $rootScope, $http, $window, ModalService) { 7 | $scope.user = {}; 8 | $scope.posts = []; 9 | 10 | var streamElement = angular.element(document.getElementById('dribbble-page'))[0]; 11 | var windowElement = angular.element($window)[0]; 12 | var currentPage = 0; 13 | var running = false; 14 | var fetching = false; 15 | 16 | function handleScroll() { 17 | if (running) { 18 | return; 19 | } 20 | 21 | running = true; 22 | requestAnimationFrame(function() { 23 | if (windowElement.scrollY + windowElement.innerHeight > streamElement.clientHeight - 200) { 24 | currentPage++; 25 | running = true; 26 | _getPosts(function() { 27 | if (!$scope.$$phase) { 28 | $scope.$apply(); 29 | } 30 | }); 31 | return; 32 | } 33 | 34 | running = false; 35 | }); 36 | } 37 | 38 | angular.element($window).bind('scroll', handleScroll); 39 | $scope.$on('$destroy', function() { 40 | angular.element($window).unbind('scroll', handleScroll); 41 | }); 42 | 43 | var emptyResponses = 0; 44 | function _getPosts(cb) { 45 | if (fetching || emptyResponses > 2) { 46 | return; 47 | } 48 | 49 | fetching = true; 50 | $http.get('/dribbble/' + currentPage).success(function(data, status) { 51 | if (status === 200 && data && data.length) { 52 | $scope.posts = $scope.posts.concat(data); 53 | 54 | fetching = false; 55 | running = false; 56 | emptyResponses = 0; 57 | cb(); 58 | } else { 59 | emptyResponses++; 60 | fetching = false; 61 | if (emptyResponses <= 2) { 62 | currentPage++; 63 | _getPosts(cb); 64 | } 65 | } 66 | }).error(function(data) { 67 | console.log('Error', data); 68 | fetching = false; 69 | running = false; 70 | cb(); 71 | }); 72 | } 73 | 74 | function _getUser(cb) { 75 | $http.get('/dribbble/user').success(function(data, status) { 76 | if (status === 200 && data) { 77 | $scope.user = data; 78 | _getPosts(cb); 79 | } 80 | }).error(function(data) { 81 | console.log('Error', data); 82 | cb(); 83 | }); 84 | } 85 | 86 | function _animateEnter(extraTime) { 87 | if (!$scope.animateEnter) { 88 | var time = $rootScope.firstEnter ? 500: 1000; 89 | time += extraTime <= 500 ? extraTime : 500; 90 | 91 | setTimeout(function() { 92 | $scope.animateEnter = true; 93 | $scope.visible = true; 94 | if (!$scope.$$phase) { 95 | $scope.$apply(); 96 | } 97 | if (!$rootScope.firstEnter) { 98 | $rootScope.firstEnter = true; 99 | } 100 | setTimeout(function() { 101 | $scope.animateEnter = false; 102 | if (!$scope.$$phase) { 103 | $scope.$apply(); 104 | } 105 | }, 1200); 106 | }, time); 107 | } 108 | } 109 | 110 | _getUser(function() { 111 | var start = new Date().getTime(); 112 | var didLoadAnimateFirst = false; 113 | 114 | var loadTimeout = setTimeout(function() { 115 | if (!didLoadAnimateFirst) { 116 | didLoadAnimateFirst = true; 117 | _animateEnter(0); 118 | } 119 | }, 2000); 120 | 121 | $rootScope.$on('lazyImg:success', function() { 122 | if (!didLoadAnimateFirst) { 123 | clearTimeout(loadTimeout); 124 | var elapsed = new Date().getTime() - start; 125 | didLoadAnimateFirst = true; 126 | _animateEnter(elapsed); 127 | return; 128 | } 129 | }); 130 | 131 | $rootScope.$on('lazyImg:error', function() { 132 | if (!didLoadAnimateFirst) { 133 | clearTimeout(loadTimeout); 134 | var elapsed = new Date().getTime() - start; 135 | didLoadAnimateFirst = true; 136 | _animateEnter(elapsed); 137 | return; 138 | } 139 | }); 140 | 141 | }); 142 | 143 | $scope.openPost = function(item) { 144 | ModalService.showModal({ 145 | templateUrl: 'templates/dribbble/details.html', 146 | controller: 'DribbbleDetailsCtrl', 147 | inputs: { 148 | item: item 149 | } 150 | }); 151 | }; 152 | } 153 | ]) 154 | 155 | .directive('dribbblePost', ['$window', 'ModalService', function($window, ModalService) { 156 | return { 157 | restrict: 'E', 158 | scope: { 159 | item: '=' 160 | }, 161 | templateUrl: 'templates/dribbble/post.html', 162 | link: function(scope) { 163 | scope.openPost = function(item) { 164 | ModalService.showModal({ 165 | templateUrl: 'templates/dribbble/details.html', 166 | controller: 'DribbbleDetailsCtrl', 167 | inputs: { 168 | item: item 169 | } 170 | }); 171 | }; 172 | } 173 | }; 174 | }]) 175 | 176 | .controller('DribbbleDetailsCtrl', ['$scope', 'close', 'item', 177 | function($scope, close, item) { 178 | $scope.close = close; 179 | $scope.item = item; 180 | } 181 | ]); -------------------------------------------------------------------------------- /client/app/scripts/github.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('clientApp') 4 | 5 | .controller('GithubCtrl', ['$scope', '$rootScope', '$http', 6 | function($scope, $rootScope, $http) { 7 | $scope.user = {}; 8 | $scope.repos = []; 9 | $scope.activities = []; 10 | 11 | var fetching = false; 12 | 13 | function _getRepos(cb) { 14 | if (fetching) { 15 | return; 16 | } 17 | 18 | fetching = true; 19 | $http.get('/github/repos').success(function(data, status) { 20 | if (status === 200 && data && data.length) { 21 | $scope.repos = data; 22 | } 23 | fetching = false; 24 | cb(); 25 | }).error(function(data) { 26 | console.log('Error', data); 27 | fetching = false; 28 | cb(); 29 | }); 30 | } 31 | 32 | function _getActivity(cb) { 33 | if (fetching) { 34 | return; 35 | } 36 | 37 | fetching = true; 38 | $http.get('/github/activity').success(function(data, status) { 39 | if (status === 200 && data && data.length) { 40 | $scope.activities = data; 41 | } 42 | fetching = false; 43 | cb(); 44 | }).error(function(data) { 45 | console.log('Error', data); 46 | fetching = false; 47 | cb(); 48 | }); 49 | } 50 | 51 | function _getUser(cb) { 52 | $http.get('/github/user').success(function(data, status) { 53 | if (status === 200 && data) { 54 | $scope.user = data; 55 | _getRepos(function() { 56 | _getActivity(cb); 57 | }); 58 | } 59 | }).error(function(data) { 60 | console.log('Error', data); 61 | cb(); 62 | }); 63 | } 64 | 65 | function _animateEnter(extraTime) { 66 | if (!$scope.animateEnter) { 67 | var time = $rootScope.firstEnter ? 500: 1000; 68 | time += extraTime <= 500 ? extraTime : 500; 69 | 70 | setTimeout(function() { 71 | $scope.animateEnter = true; 72 | $scope.visible = true; 73 | if (!$scope.$$phase) { 74 | $scope.$apply(); 75 | } 76 | if (!$rootScope.firstEnter) { 77 | $rootScope.firstEnter = true; 78 | } 79 | setTimeout(function() { 80 | $scope.animateEnter = false; 81 | if (!$scope.$$phase) { 82 | $scope.$apply(); 83 | } 84 | }, 1200); 85 | }, time); 86 | } 87 | } 88 | 89 | _getUser(function() { 90 | _animateEnter(0); 91 | }); 92 | } 93 | ]) 94 | 95 | 96 | .directive('githubActivity', function() { 97 | return { 98 | restrict: 'E', 99 | scope: { 100 | item: '=' 101 | }, 102 | templateUrl: 'templates/github/activity.html' 103 | }; 104 | }); -------------------------------------------------------------------------------- /client/app/scripts/instagram.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('clientApp') 4 | 5 | .controller('InstagramCtrl', ['$scope', '$rootScope', '$http', '$window', 'ModalService', 6 | function($scope, $rootScope, $http, $window, ModalService) { 7 | $scope.user = {}; 8 | $scope.posts = []; 9 | 10 | var running = false; 11 | var fetching = false; 12 | 13 | function _getPosts(cb) { 14 | if (fetching) { 15 | return; 16 | } 17 | 18 | fetching = true; 19 | $http.get('/instagram/recent').success(function(data, status) { 20 | if (status === 200 && data && data.length) { 21 | $scope.posts = $scope.posts.concat(data); 22 | fetching = false; 23 | running = false; 24 | cb(); 25 | } 26 | }).error(function(data) { 27 | console.log('Error', data); 28 | fetching = false; 29 | running = false; 30 | cb(); 31 | }); 32 | } 33 | 34 | function _getUser(cb) { 35 | $http.get('/instagram/user').success(function(data, status) { 36 | if (status === 200 && data) { 37 | $scope.user = data; 38 | _getPosts(cb); 39 | } 40 | }).error(function(data) { 41 | console.log('Error', data); 42 | cb(); 43 | }); 44 | } 45 | 46 | function _animateEnter(extraTime) { 47 | if (!$scope.animateEnter) { 48 | var time = $rootScope.firstEnter ? 500: 1000; 49 | time += extraTime <= 500 ? extraTime : 500; 50 | 51 | setTimeout(function() { 52 | $scope.animateEnter = true; 53 | $scope.visible = true; 54 | if (!$scope.$$phase) { 55 | $scope.$apply(); 56 | } 57 | if (!$rootScope.firstEnter) { 58 | $rootScope.firstEnter = true; 59 | } 60 | setTimeout(function() { 61 | $scope.animateEnter = false; 62 | if (!$scope.$$phase) { 63 | $scope.$apply(); 64 | } 65 | }, 1200); 66 | }, time); 67 | } 68 | } 69 | 70 | _getUser(function() { 71 | var start = new Date().getTime(); 72 | var didLoadAnimateFirst = false; 73 | 74 | var loadTimeout = setTimeout(function() { 75 | if (!didLoadAnimateFirst) { 76 | didLoadAnimateFirst = true; 77 | _animateEnter(0); 78 | } 79 | }, 2000); 80 | 81 | $rootScope.$on('lazyImg:success', function() { 82 | if (!didLoadAnimateFirst) { 83 | clearTimeout(loadTimeout); 84 | var elapsed = new Date().getTime() - start; 85 | didLoadAnimateFirst = true; 86 | _animateEnter(elapsed); 87 | return; 88 | } 89 | }); 90 | 91 | $rootScope.$on('lazyImg:error', function() { 92 | if (!didLoadAnimateFirst) { 93 | clearTimeout(loadTimeout); 94 | var elapsed = new Date().getTime() - start; 95 | didLoadAnimateFirst = true; 96 | _animateEnter(elapsed); 97 | return; 98 | } 99 | }); 100 | 101 | }); 102 | 103 | $scope.openPost = function(item) { 104 | ModalService.showModal({ 105 | templateUrl: 'templates/instagram/details.html', 106 | controller: 'InstagramDetailsCtrl', 107 | inputs: { 108 | item: item 109 | } 110 | }); 111 | }; 112 | } 113 | ]) 114 | 115 | .directive('instagramPost', ['$window', 'ModalService', function($window, ModalService) { 116 | return { 117 | restrict: 'E', 118 | scope: { 119 | item: '=' 120 | }, 121 | templateUrl: 'templates/instagram/post.html', 122 | link: function(scope) { 123 | scope.openPost = function(item) { 124 | ModalService.showModal({ 125 | templateUrl: 'templates/instagram/details.html', 126 | controller: 'InstagramDetailsCtrl', 127 | inputs: { 128 | item: item 129 | } 130 | }); 131 | }; 132 | } 133 | }; 134 | }]) 135 | 136 | .controller('InstagramDetailsCtrl', ['$scope', 'close', 'item', 137 | function($scope, close, item) { 138 | $scope.close = close; 139 | $scope.item = item; 140 | $scope.playing = false; 141 | 142 | $scope.playVideo = function() { 143 | var video = angular.element(document.querySelector('#video')); 144 | if (video[0].paused === true) { 145 | video[0].play(); 146 | $scope.playing = true; 147 | } else { 148 | video[0].pause(); 149 | $scope.playing = false; 150 | } 151 | 152 | video[0].onended = function() { 153 | $scope.playing = false; 154 | if (!$scope.$$phase) { 155 | $scope.$apply(); 156 | } 157 | }; 158 | }; 159 | } 160 | ]); 161 | -------------------------------------------------------------------------------- /client/app/scripts/lastfm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('clientApp') 4 | 5 | .controller('LastfmCtrl', ['$scope', '$rootScope', '$http', '$window', 6 | function($scope, $rootScope, $http, $window) { 7 | $scope.user = {}; 8 | $scope.items = []; 9 | 10 | var fetching = false; 11 | 12 | function _getTracks(cb) { 13 | if (fetching) { 14 | return; 15 | } 16 | 17 | fetching = true; 18 | $http.get('/lastfm/activity').success(function(data, status) { 19 | if (status === 200 && data && data.length) { 20 | $scope.items = $scope.items.concat(data); 21 | fetching = false; 22 | } 23 | cb(); 24 | }).error(function(data) { 25 | console.log('Error', data); 26 | fetching = false; 27 | cb(); 28 | }); 29 | } 30 | 31 | function _geTopStats(cb) { 32 | if (fetching) { 33 | return; 34 | } 35 | 36 | fetching = true; 37 | $http.get('/lastfm/top').success(function(data, status) { 38 | if (status === 200 && data) { 39 | $scope.topArtists = data.artists ? data.artists : null; 40 | $scope.topAlbums = data.albums ? data.albums : null; 41 | $scope.topTracks = data.tracks ? data.tracks : null; 42 | if (!$scope.$$phase) { 43 | $scope.$apply(); 44 | } 45 | fetching = false; 46 | } 47 | cb(); 48 | }).error(function(data) { 49 | console.log('Error', data); 50 | fetching = false; 51 | cb(); 52 | }); 53 | } 54 | 55 | function _getUser(cb) { 56 | $http.get('/lastfm/user').success(function(data, status) { 57 | if (status === 200 && data) { 58 | $scope.user = data; 59 | _geTopStats(function() { 60 | _getTracks(cb); 61 | }); 62 | } 63 | }).error(function(data) { 64 | console.log('Error', data); 65 | cb(); 66 | }); 67 | } 68 | 69 | function _animateEnter(extraTime) { 70 | if (!$scope.animateEnter) { 71 | var time = $rootScope.firstEnter ? 500: 1000; 72 | time += extraTime <= 500 ? extraTime : 500; 73 | 74 | setTimeout(function() { 75 | $scope.animateEnter = true; 76 | $scope.visible = true; 77 | 78 | //for some reason the track images sometimes don't show on page load, this forces it... 79 | $rootScope.$emit('lazyImg:refresh'); 80 | 81 | if (!$scope.$$phase) { 82 | $scope.$apply(); 83 | } 84 | if (!$rootScope.firstEnter) { 85 | $rootScope.firstEnter = true; 86 | } 87 | setTimeout(function() { 88 | $scope.animateEnter = false; 89 | if (!$scope.$$phase) { 90 | $scope.$apply(); 91 | } 92 | }, 1200); 93 | }, time); 94 | } 95 | } 96 | 97 | _getUser(function() { 98 | var start = new Date().getTime(); 99 | var didLoadAnimateFirst = false; 100 | 101 | var loadTimeout = setTimeout(function() { 102 | if (!didLoadAnimateFirst) { 103 | didLoadAnimateFirst = true; 104 | _animateEnter(0); 105 | } 106 | }, 2000); 107 | 108 | $rootScope.$on('lazyImg:success', function() { 109 | if (!didLoadAnimateFirst) { 110 | clearTimeout(loadTimeout); 111 | var elapsed = new Date().getTime() - start; 112 | didLoadAnimateFirst = true; 113 | _animateEnter(elapsed); 114 | return; 115 | } 116 | }); 117 | 118 | $rootScope.$on('lazyImg:error', function() { 119 | if (!didLoadAnimateFirst) { 120 | clearTimeout(loadTimeout); 121 | var elapsed = new Date().getTime() - start; 122 | didLoadAnimateFirst = true; 123 | _animateEnter(elapsed); 124 | return; 125 | } 126 | }); 127 | }); 128 | 129 | $scope.openTop = function(item) { 130 | if (item.url) { 131 | $window.open(item.url); 132 | } 133 | }; 134 | } 135 | ]) 136 | 137 | .directive('lastfmActivity', function() { 138 | return { 139 | restrict: 'E', 140 | scope: { 141 | item: '=' 142 | }, 143 | templateUrl: 'templates/lastfm/activity.html' 144 | }; 145 | }); -------------------------------------------------------------------------------- /client/app/scripts/sample-env.js: -------------------------------------------------------------------------------- 1 | /*jshint unused:false*/ 2 | 3 | var GOOGLE_MAPS_KEY = ''; 4 | var GOOGLE_ANALYTICS_KEY = ''; 5 | -------------------------------------------------------------------------------- /client/app/scripts/stream.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('clientApp') 4 | .controller('StreamCtrl', ['$window', '$scope', '$rootScope', '$filter', '$http', 'moment', 5 | function($window, $scope, $rootScope, $filter, $http, moment) { 6 | $scope.groups = {}; 7 | 8 | var streamElement = angular.element(document.getElementById('main-stream'))[0]; 9 | var windowElement = angular.element($window)[0]; 10 | var currentPage = 0; 11 | var running = false; 12 | var fetching = false; 13 | 14 | function handleScroll() { 15 | if (running) { 16 | return; 17 | } 18 | 19 | running = true; 20 | requestAnimationFrame(function() { 21 | if (windowElement.scrollY + windowElement.innerHeight > streamElement.clientHeight - 200) { 22 | currentPage++; 23 | running = true; 24 | _getStream(function() { 25 | if (!$scope.$$phase) { 26 | $scope.$apply(); 27 | } 28 | }); 29 | return; 30 | } 31 | 32 | running = false; 33 | }); 34 | } 35 | 36 | angular.element($window).bind('scroll', handleScroll); 37 | $scope.$on('$destroy', function() { 38 | angular.element($window).unbind('scroll', handleScroll); 39 | }); 40 | 41 | var todayFormated = moment().format('YYYY-MM-DD'); 42 | var yesterdayFormatted = moment().subtract(1, 'day').format('YYYY-MM-DD'); 43 | var currentYear = moment().format('YYYY'); 44 | 45 | var emptyResponses = 0; 46 | function _getStream(cb) { 47 | if (fetching || emptyResponses > 2) { 48 | return; 49 | } 50 | 51 | fetching = true; 52 | $http.get('/stream/' + currentPage).success(function(data, status) { 53 | if (status === 200 && data && data.length) { 54 | for (var i=0; i streamElement.clientHeight - 200) { 24 | currentPage++; 25 | running = true; 26 | _getPosts(function() { 27 | if (!$scope.$$phase) { 28 | $scope.$apply(); 29 | } 30 | }); 31 | return; 32 | } 33 | 34 | running = false; 35 | }); 36 | } 37 | 38 | angular.element($window).bind('scroll', handleScroll); 39 | $scope.$on('$destroy', function() { 40 | angular.element($window).unbind('scroll', handleScroll); 41 | }); 42 | 43 | var emptyResponses = 0; 44 | function _getPosts(cb) { 45 | if (fetching || emptyResponses > 2) { 46 | return; 47 | } 48 | 49 | fetching = true; 50 | $http.get('/youtube/' + currentPage).success(function(data, status) { 51 | if (status === 200 && data && data.length) { 52 | $scope.posts = $scope.posts.concat(data); 53 | 54 | fetching = false; 55 | running = false; 56 | emptyResponses = 0; 57 | cb(); 58 | } else { 59 | emptyResponses++; 60 | fetching = false; 61 | if (emptyResponses <= 2) { 62 | currentPage++; 63 | _getPosts(cb); 64 | } 65 | } 66 | }).error(function(data) { 67 | console.log('Error', data); 68 | fetching = false; 69 | running = false; 70 | cb(); 71 | }); 72 | } 73 | 74 | function _getUser(cb) { 75 | $http.get('/youtube/user').success(function(data, status) { 76 | if (status === 200 && data) { 77 | $scope.user = data; 78 | _getPosts(cb); 79 | } 80 | }).error(function(data) { 81 | console.log('Error', data); 82 | cb(); 83 | }); 84 | } 85 | 86 | function _animateEnter(extraTime) { 87 | if (!$scope.animateEnter) { 88 | var time = $rootScope.firstEnter ? 500: 1000; 89 | time += extraTime <= 500 ? extraTime : 500; 90 | 91 | setTimeout(function() { 92 | $scope.animateEnter = true; 93 | $scope.visible = true; 94 | if (!$scope.$$phase) { 95 | $scope.$apply(); 96 | } 97 | if (!$rootScope.firstEnter) { 98 | $rootScope.firstEnter = true; 99 | } 100 | setTimeout(function() { 101 | $scope.animateEnter = false; 102 | if (!$scope.$$phase) { 103 | $scope.$apply(); 104 | } 105 | }, 1200); 106 | }, time); 107 | } 108 | } 109 | 110 | _getUser(function() { 111 | var start = new Date().getTime(); 112 | var didLoadAnimateFirst = false; 113 | 114 | var loadTimeout = setTimeout(function() { 115 | if (!didLoadAnimateFirst) { 116 | didLoadAnimateFirst = true; 117 | _animateEnter(0); 118 | } 119 | }, 2000); 120 | 121 | $rootScope.$on('lazyImg:success', function() { 122 | if (!didLoadAnimateFirst) { 123 | clearTimeout(loadTimeout); 124 | var elapsed = new Date().getTime() - start; 125 | didLoadAnimateFirst = true; 126 | _animateEnter(elapsed); 127 | return; 128 | } 129 | }); 130 | 131 | $rootScope.$on('lazyImg:error', function() { 132 | if (!didLoadAnimateFirst) { 133 | clearTimeout(loadTimeout); 134 | var elapsed = new Date().getTime() - start; 135 | didLoadAnimateFirst = true; 136 | _animateEnter(elapsed); 137 | return; 138 | } 139 | }); 140 | 141 | }); 142 | 143 | $scope.openPost = function(item) { 144 | ModalService.showModal({ 145 | templateUrl: 'templates/youtube/details.html', 146 | controller: 'YoutubeDetailsCtrl', 147 | inputs: { 148 | item: item 149 | } 150 | }); 151 | }; 152 | } 153 | ]) 154 | 155 | .directive('youtubePost', ['$window', 'ModalService', function($window, ModalService) { 156 | return { 157 | restrict: 'E', 158 | scope: { 159 | item: '=' 160 | }, 161 | templateUrl: 'templates/youtube/post.html', 162 | link: function(scope) { 163 | scope.openPost = function(item) { 164 | ModalService.showModal({ 165 | templateUrl: 'templates/youtube/details.html', 166 | controller: 'YoutubeDetailsCtrl', 167 | inputs: { 168 | item: item 169 | } 170 | }); 171 | }; 172 | } 173 | }; 174 | }]) 175 | 176 | .controller('YoutubeDetailsCtrl', ['$scope', 'close', 'item', 177 | function($scope, close, item) { 178 | $scope.close = close; 179 | $scope.item = item; 180 | $scope.youtubeEmbed = 'https://www.youtube.com/embed/' + item.id; 181 | } 182 | ]); -------------------------------------------------------------------------------- /client/app/styles/less/app.less: -------------------------------------------------------------------------------- 1 | @import 'base.less'; 2 | @import 'components/headers.less'; 3 | @import 'components/icons.less'; 4 | @import 'components/modals.less'; 5 | @import 'components/animations.less'; 6 | 7 | .main-body { 8 | width: 95%; 9 | max-width: 1140px; 10 | position: relative; 11 | margin: 0 auto; 12 | } 13 | 14 | .main-header { 15 | position: fixed; 16 | z-index: 10; 17 | margin-top: 40px; 18 | width: 15%; 19 | height: 100%; 20 | 21 | hgroup a { 22 | display: block; 23 | color: @text-color; 24 | } 25 | 26 | .about-link { 27 | display: inline-block; 28 | margin: 20px; 29 | font-size: 12px; 30 | position: absolute; 31 | bottom: 30px; 32 | .transition(e('all 320ms ease-out')); 33 | 34 | &:hover { 35 | .transform(@transform:scale(1.1)); 36 | } 37 | } 38 | 39 | .picture { 40 | width: 50px; 41 | height: 50px; 42 | .border-radius(25px); 43 | overflow: hidden; 44 | display: inline-block; 45 | margin-right: 20px; 46 | .box-shadow(@shadow: 0 2px 6px rgba(0,0,0,.9)); 47 | 48 | img { 49 | width: 50px; 50 | height: 50px; 51 | } 52 | } 53 | 54 | h1 { 55 | display: inline-block; 56 | vertical-align: top; 57 | line-height: 50px; 58 | text-transform: uppercase; 59 | position: relative; 60 | } 61 | } 62 | 63 | .main-nav { 64 | margin-top: 20px; 65 | 66 | li { 67 | padding: 10px; 68 | 69 | .txt { 70 | opacity: 0.7; 71 | .transition(e('all 320ms ease-out')); 72 | } 73 | 74 | .icon { 75 | opacity: 0.7; 76 | .transition(e('all 320ms ease-out')); 77 | } 78 | } 79 | 80 | li:hover { 81 | .txt { 82 | opacity: 1; 83 | } 84 | 85 | .icon { 86 | opacity: 1; 87 | .transform(@transform:scale(1.1)); 88 | } 89 | } 90 | 91 | .icon.twitter { 92 | vertical-align: middle; 93 | } 94 | 95 | .icon.instagram { 96 | font-size: 23px; 97 | } 98 | 99 | .icon.foursquare { 100 | font-size: 21px; 101 | } 102 | 103 | .icon.tumblr { 104 | font-size: 24px; 105 | } 106 | 107 | .txt { 108 | line-height: 30px; 109 | margin-left: 15px; 110 | font-weight: 400; 111 | position: relative; 112 | } 113 | } 114 | 115 | .main-header.animate { 116 | hgroup { 117 | .animation(e('fade-in 500ms ease-in backwards')); 118 | } 119 | 120 | .about-link { 121 | .animation(e('fade-in 500ms ease-in 1400ms backwards')); 122 | } 123 | 124 | .nav-loop(@counter) when (@counter > 0) { 125 | .main-nav li:nth-child(@{counter}) { 126 | .icon { .animation(e('scale-in 450ms ease-out @{counter}00ms 1 backwards')); }; 127 | .txt { .animation(e('fade-in 450ms ease-out @{counter}00ms 1 backwards')); }; 128 | } 129 | 130 | .nav-loop((@counter - 1)); 131 | } 132 | .nav-loop(8); 133 | } 134 | 135 | .main-view { 136 | margin-left: 21%; 137 | position: relative; 138 | } 139 | 140 | .main-view.ng-enter { 141 | position: absolute; 142 | top: 0; 143 | left: 0; 144 | right: 0; 145 | .animation(e('fade-in 500ms ease-in 500ms')); 146 | } 147 | 148 | .main-view.ng-leave { 149 | .animation(e('fade-out 500ms')); 150 | } 151 | 152 | .loading-view { 153 | position: fixed; 154 | left: 0; 155 | top: 0; 156 | right: 0; 157 | bottom: 0; 158 | z-index: 100; 159 | display: table; 160 | height: 100%; 161 | width: 100%; 162 | 163 | &.hidden { 164 | display: none; 165 | } 166 | 167 | span { 168 | font-weight: 400; 169 | text-transform: uppercase; 170 | margin: 0 auto; 171 | display: table-cell; 172 | vertical-align: middle; 173 | text-align: center; 174 | padding: 0 40px; 175 | opacity: 0.6; 176 | } 177 | } 178 | 179 | .button { 180 | display: inline-block; 181 | padding: 10px 20px; 182 | margin-bottom: 20px; 183 | background: @dark-bg-color; 184 | color: @text-color-strong; 185 | .transition(e('all 320ms ease-out')); 186 | 187 | &:hover { 188 | background: @link-color; 189 | color: @bg-color; 190 | .transform(@transform:scale(1.04)); 191 | } 192 | } 193 | 194 | @media (max-width: 1035px) { 195 | .main-header { 196 | width: 50px; 197 | height: auto; 198 | 199 | h1 { 200 | display: none; 201 | } 202 | 203 | .about-link { 204 | display: none; 205 | } 206 | } 207 | 208 | .main-nav li .txt { 209 | display: none; 210 | } 211 | 212 | .main-view { 213 | margin-left: 80px; 214 | } 215 | 216 | .main-view.ng-enter { 217 | left: 30px; 218 | right: 30px; 219 | } 220 | 221 | .main-body { 222 | width: 100%; 223 | padding: 0 30px; 224 | } 225 | } 226 | 227 | @media (max-width: 480px) { 228 | .main-header { 229 | width: 100%; 230 | margin-top: 0; 231 | position: fixed; 232 | height: 50px; 233 | background: rgba(24, 33, 37, 0.9); 234 | 235 | h1 { 236 | display: block; 237 | text-align: center; 238 | } 239 | 240 | .picture { 241 | display: none; 242 | } 243 | } 244 | 245 | .main-body { 246 | padding: 0; 247 | } 248 | 249 | .main-view { 250 | margin-left: 0; 251 | padding: 20px 10px; 252 | 253 | &.ng-enter { 254 | left: 0px; 255 | right: 0px; 256 | } 257 | } 258 | 259 | .main-nav { 260 | display: none; 261 | margin-top: 0; 262 | margin-bottom: 20px; 263 | 264 | li { 265 | padding: 10px 20px; 266 | 267 | .txt { 268 | display: inline-block; 269 | } 270 | } 271 | } 272 | 273 | .nav-btn { 274 | height: 30px; 275 | position: absolute; 276 | left: 0; 277 | top: 10px; 278 | margin: 0 5px; 279 | padding: 10px; 280 | z-index: 1; 281 | cursor: pointer; 282 | 283 | &:active { opacity: 0.5; } 284 | } 285 | 286 | .nav-btn-bar { 287 | .transition(all .2s ease-in-out); 288 | display: block; 289 | width: 24px; 290 | height: 1px; 291 | margin-bottom: 4px; 292 | background-color: @text-color; 293 | } 294 | 295 | .main-header.open { 296 | height: auto; 297 | 298 | .main-nav { 299 | display: block; 300 | } 301 | 302 | .nav-btn { 303 | top: 13px; 304 | 305 | .nb-1 { 306 | .transform(rotate(45deg)); 307 | } 308 | 309 | .nb-2 { 310 | opacity: 0; 311 | } 312 | 313 | .nb-3 { 314 | margin-top: -10px; 315 | .transform(rotate(-45deg)); 316 | } 317 | } 318 | } 319 | } 320 | 321 | @media (max-height: 600px) { 322 | .main-header .about-link { 323 | display: none; 324 | } 325 | 326 | .main-nav { 327 | margin-top: 10px; 328 | 329 | li { 330 | padding: 7px 10px; 331 | } 332 | } 333 | } 334 | 335 | @media (max-height: 480px) { 336 | .main-header { 337 | margin-top: 20px; 338 | 339 | .picture { 340 | display: none; 341 | } 342 | } 343 | } 344 | 345 | @media (max-height: 400px) and (min-width: 480px) { 346 | .main-header { 347 | margin-top: 0px; 348 | 349 | h1 { 350 | display: none; 351 | } 352 | } 353 | } 354 | 355 | @import 'pages/stream.less'; 356 | @import 'pages/twitter.less'; 357 | @import 'pages/dribbble.less'; 358 | @import 'pages/instagram.less'; 359 | @import 'pages/youtube.less'; 360 | @import 'pages/github.less'; 361 | @import 'pages/tumblr.less'; 362 | @import 'pages/foursquare.less'; 363 | @import 'pages/lastfm.less'; 364 | -------------------------------------------------------------------------------- /client/app/styles/less/base.less: -------------------------------------------------------------------------------- 1 | @import "components/reset.less"; 2 | @import "components/mixins.less"; 3 | @import "variables.less"; 4 | 5 | @font-face { 6 | font-family: 'icons'; 7 | src:url("../fonts/icons.eot?omeqz4"); 8 | src:url("../fonts/icons.eot?omeqz4#iefix") format('embedded-opentype'), 9 | url("../fonts/icons.woff?omeqz4") format('woff'), 10 | url("../fonts/icons.ttf?omeqz4") format('truetype'), 11 | url("../fonts/icons.svg?omeqz4#icons") format('svg'); 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | 16 | html { 17 | -webkit-font-smoothing:antialiased; 18 | } 19 | 20 | body { 21 | background-color: @bg-color; 22 | font-family: "Lato", "Helvetica Neue", "Arial", sans-serif; 23 | font-size: 14px; 24 | line-height: 24px; 25 | font-weight: 300; 26 | color: @text-color; 27 | 28 | -ms-content-zooming: none; 29 | -ms-overflow-style: -ms-autohiding-scrollbar; 30 | } 31 | 32 | ::selection { 33 | background: @link-color; 34 | color: #fff; 35 | text-shadow: none; 36 | } 37 | 38 | strong, b { 39 | font-weight: 400; 40 | color: @text-color-strong; 41 | } 42 | 43 | a { 44 | color: @link-color; 45 | text-decoration: none; 46 | cursor: pointer; 47 | } 48 | 49 | p { 50 | margin: 0 0 15px 0; 51 | } 52 | 53 | blockquote { 54 | margin: 10px; 55 | padding: 0 0 0 15px; 56 | border-left: 3px solid @link-color; 57 | } 58 | 59 | em { 60 | font-style: italic; 61 | } 62 | 63 | iframe { 64 | display: none; 65 | } 66 | 67 | .pre { 68 | white-space: pre; 69 | } 70 | 71 | /* IE snapped view fix */ 72 | @-ms-viewport { width: device-width; } 73 | -------------------------------------------------------------------------------- /client/app/styles/less/components/animations.less: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes scale-in { 2 | 0% { -webkit-transform: scale(0); opacity: 0;} 3 | 100% { -webkit-transform: scale(1); opacity: 1; } 4 | } 5 | @keyframes scale-in { 6 | 0% { transform: scale(0); opacity: 0;} 7 | 100% { transform: scale(1); opacity: 1;} 8 | } 9 | 10 | @-webkit-keyframes scale-out { 11 | 0% { -webkit-transform: scale(1); opacity: 1;} 12 | 100% { -webkit-transform: scale(1.5); opacity: 0;} 13 | } 14 | @keyframes scale-out { 15 | 0% { transform: scale(1); opacity: 1;} 16 | 100% { transform: scale(1.5); opacity: 0} 17 | } 18 | 19 | @-webkit-keyframes half-scale-in { 20 | 0% { -webkit-transform: scale(0.7); opacity: 0;} 21 | 100% { -webkit-transform: scale(1); opacity: 1; } 22 | } 23 | @keyframes half-scale-in { 24 | 0% { transform: scale(0.7); opacity: 0;} 25 | 100% { transform: scale(1); opacity: 1;} 26 | } 27 | 28 | @-webkit-keyframes more-half-scale-in { 29 | 0% { -webkit-transform: scale(0.9); opacity: 0;} 30 | 100% { -webkit-transform: scale(1); opacity: 1; } 31 | } 32 | @keyframes more-half-scale-in { 33 | 0% { transform: scale(0.9); opacity: 0;} 34 | 100% { ransform: scale(1); opacity: 1;} 35 | } 36 | 37 | @keyframes fade-in { 38 | 0% { opacity: 0; } 39 | 100% { opacity: 1; } 40 | } 41 | @-moz-keyframes fade-in { 42 | 0% { opacity: 0; } 43 | 100% { opacity: 1; } 44 | } 45 | @-webkit-keyframes fade-in { 46 | 0% { opacity: 0; } 47 | 100% { opacity: 1; } 48 | } 49 | 50 | @keyframes fade-out { 51 | 0% { opacity: 1; } 52 | 100% { opacity: 0; } 53 | } 54 | @-moz-keyframes fade-out { 55 | 0% { opacity: 1; } 56 | 100% { opacity: 0; } 57 | } 58 | @-webkit-keyframes fade-out { 59 | 0% { opacity: 1; } 60 | 100% { opacity: 0; } 61 | } 62 | 63 | @-webkit-keyframes move-in { 64 | 0% { left: -20px; opacity: 0; } 65 | 100% { left: 0px; opacity: 1;} 66 | } 67 | 68 | @keyframes move-in { 69 | 0% { left: -20px; opacity: 0;} 70 | 100% { left: 0px; opacity: 1;} 71 | } 72 | 73 | @-webkit-keyframes move-out { 74 | 0% { left: 0px; opacity: 1; } 75 | 100% { left: 20px; opacity: 0;} 76 | } 77 | 78 | @keyframes move-out { 79 | 0% { left: 0px; opacity: 1;} 80 | 100% { left: 20px; opacity: 0;} 81 | } -------------------------------------------------------------------------------- /client/app/styles/less/components/headers.less: -------------------------------------------------------------------------------- 1 | /* Headers */ 2 | 3 | h1, h2, h3, h4, h5, h6, 4 | .h1, .h2, .h3, .h4, .h5, .h6 { 5 | font-weight: 400; 6 | font-style: normal; 7 | } 8 | 9 | h1, .h1 { font-size: 24px; line-height: 36px; } 10 | h2, .h2 { font-size: 18px; line-height: 28px; } 11 | h3, .h3 { font-size: 16px; line-height: 28px; } 12 | h4, .h4 { font-size: 14px; line-height: 26px; } 13 | -------------------------------------------------------------------------------- /client/app/styles/less/components/icons.less: -------------------------------------------------------------------------------- 1 | .icon { 2 | width: 30px; 3 | height: 30px; 4 | display: inline-block; 5 | position: relative; 6 | overflow: visible; 7 | vertical-align: top; 8 | color: @link-color; 9 | font-family: 'icons'; 10 | font-size: 26px; 11 | line-height: 30px; 12 | text-align: center; 13 | speak: none; 14 | 15 | &.twitter:before { 16 | content: "\e90a"; 17 | } 18 | 19 | &.facebook:before { 20 | content: "\e909"; 21 | } 22 | 23 | &.tumblr:before { 24 | content: "\e908"; 25 | } 26 | 27 | &.spotify:before { 28 | content: "\e907"; 29 | } 30 | 31 | &.instagram:before { 32 | content: "\e906"; 33 | } 34 | 35 | &.foursquare:before { 36 | content: "\e905"; 37 | } 38 | 39 | &.dribbble:before { 40 | content: "\e904"; 41 | } 42 | 43 | &.youtube:before { 44 | content: "\e903"; 45 | } 46 | 47 | &.linkedin:before { 48 | content: "\e902"; 49 | } 50 | 51 | &.github:before { 52 | content: "\e901"; 53 | } 54 | 55 | &.flickr:before { 56 | content: "\e900"; 57 | } 58 | 59 | &.play-video { 60 | height: 50px; 61 | width: 50px; 62 | color: #fff; 63 | line-height: 50px; 64 | font-size: 50px; 65 | 66 | &:before { 67 | content: "\e916"; 68 | } 69 | } 70 | 71 | &.play { 72 | height: 50px; 73 | width: 50px; 74 | color: #fff; 75 | line-height: 50px; 76 | font-size: 50px; 77 | 78 | &:before { 79 | content: "\e91b"; 80 | } 81 | } 82 | 83 | &.right:before { 84 | content: "\e91d"; 85 | } 86 | 87 | &.left:before { 88 | content: "\e91e"; 89 | } 90 | 91 | &.star { 92 | font-size: 18px; 93 | line-height: 24px; 94 | 95 | &:before { 96 | content: "\e919"; 97 | } 98 | } 99 | 100 | &.comment:before { 101 | content: "\f04f"; 102 | } 103 | 104 | &.git-branch:before { 105 | content: "\f020"; 106 | } 107 | 108 | &.git-commit:before { 109 | content: "\f01f"; 110 | } 111 | 112 | &.git-merge:before { 113 | content: "\f023"; 114 | } 115 | 116 | &.git-pull:before { 117 | content: "\f009"; 118 | } 119 | 120 | &.git-repo:before { 121 | content: "\f001"; 122 | } 123 | 124 | &.git-issue-closed:before { 125 | content: "\f028"; 126 | } 127 | 128 | &.git-issue:before { 129 | content: "\f026"; 130 | } 131 | 132 | &.git-issue-reopened:before { 133 | content: "\f027"; 134 | } 135 | 136 | &.git-tag:before { 137 | content: "\f015"; 138 | } 139 | 140 | &.music:before { 141 | content: "\e91f"; 142 | } 143 | 144 | } 145 | 146 | .small-icon { 147 | width: 20px; 148 | height: 20px; 149 | display: inline-block; 150 | position: relative; 151 | overflow: visible; 152 | vertical-align: top; 153 | color: @text-color; 154 | font-family: 'icons'; 155 | font-size: 18px; 156 | line-height: 20px; 157 | text-align: center; 158 | speak: none; 159 | 160 | &.twitter:before { 161 | content: "\e90e"; 162 | } 163 | 164 | &.facebook:before { 165 | content: "\e90b"; 166 | } 167 | 168 | &.tumblr:before { 169 | content: "\e913"; 170 | } 171 | 172 | &.spotify:before { 173 | content: "\e90d"; 174 | } 175 | 176 | &.instagram:before { 177 | content: "\e90c"; 178 | } 179 | 180 | &.foursquare:before { 181 | content: "\e915"; 182 | } 183 | 184 | &.dribbble:before { 185 | content: "\e911"; 186 | } 187 | 188 | &.youtube:before { 189 | content: "\e90f"; 190 | } 191 | 192 | &.linkedin:before { 193 | content: "\e914"; 194 | } 195 | 196 | &.github:before { 197 | content: "\e912"; 198 | } 199 | 200 | &.flickr:before { 201 | content: "\e910"; 202 | } 203 | 204 | &.comment:before { 205 | content: "\e917"; 206 | } 207 | 208 | &.views:before { 209 | content: "\e918"; 210 | } 211 | 212 | &.star:before { 213 | content: "\e919"; 214 | } 215 | 216 | &.heart:before { 217 | content: "\e91a"; 218 | } 219 | 220 | &.loop:before { 221 | content: "\e91c"; 222 | } 223 | 224 | &.play { 225 | font-size: 16px; 226 | text-indent: 3px; 227 | 228 | &:before { 229 | content: "\e91b"; 230 | } 231 | } 232 | 233 | &.git-branch:before { 234 | content: "\f020"; 235 | } 236 | } -------------------------------------------------------------------------------- /client/app/styles/less/components/mixins.less: -------------------------------------------------------------------------------- 1 | // Clearfix for clearing floats 2 | .clearfix { 3 | zoom: 1; 4 | &:before, &:after { display: table; content: "";} 5 | &:after {clear: both;} 6 | } 7 | 8 | // Border Radius 9 | .border-radius(@radius: 5px) { 10 | -webkit-border-radius: @radius; 11 | border-radius: @radius; 12 | } 13 | 14 | // Custom Border Radius 15 | .border-radius-custom(@topleft: 5px, @topright: 5px, @bottomleft: 5px, @bottomright: 5px) { 16 | -webkit-border-radius: @topleft @topright @bottomright @bottomleft; 17 | border-radius: @topleft @topright @bottomright @bottomleft; 18 | } 19 | 20 | // Drop shadows 21 | .box-shadow(@shadow: 0 1px 3px rgba(0,0,0,.25)) { 22 | -webkit-box-shadow: @shadow; 23 | box-shadow: @shadow; 24 | } 25 | 26 | .double-box-shadows(@shadow1: inset 0px 0px 2px rgba(255, 255, 255, 0.7), @shadow2: 0px 1px 3px rgba(0, 0, 0, 0.4)) { 27 | -webkit-box-shadow: @shadow1, @shadow2; 28 | box-shadow: @shadow1, @shadow2; 29 | } 30 | 31 | //3D Transform 32 | .transform(@transform:rotateY(0deg)) { 33 | -webkit-transform: @transform; 34 | -webkit-transform-style: preserve-3d; 35 | -webkit-backface-visibility: hidden; 36 | transform: @transform; 37 | } 38 | 39 | // Transitions 40 | .transition(@transition) { 41 | -webkit-transition: @transition; 42 | transition: @transition; 43 | } 44 | 45 | .transition-2(@transition1, @transition2) { 46 | -webkit-transition: @transition1, @transition2; 47 | transition: @transition1, @transition2; 48 | } 49 | 50 | .animation(@animation) { 51 | -webkit-animation: @animation; 52 | -moz-animation: @animation; 53 | animation: @animation; 54 | } 55 | 56 | // Gradients 57 | .gradient (@startColor: #555, @endColor: #333) { 58 | background-color: @endColor; 59 | background-repeat: repeat-x; 60 | filter: ~"progid:DXImageTransform.Microsoft.gradient(startColorstr='@{startColor}', endColorstr='@{endColor}',GradientType=0)"; // IE6 - 9 61 | background-image: -khtml-gradient(linear, left top, left bottom, from(@startColor), to(@endColor)); // Konqueror 62 | background-image: -ms-linear-gradient(top, @startColor, @endColor); // IE10 63 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, @startColor), color-stop(100%, @endColor)); // Safari 4+, Chrome 2+ 64 | background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+ 65 | background-image: linear-gradient(top, @startColor, @endColor); // The standard 66 | } 67 | 68 | .horizontal-gradient (@startColor: #555, @endColor: #333) { 69 | background-color: @endColor; 70 | background-repeat: repeat-x; 71 | filter: ~"progid:DXImageTransform.Microsoft.gradient(startColorstr='@{startColor}', endColorstr='@{endColor}',GradientType=1)"; // IE6 - 9 72 | background-image: -khtml-gradient(linear, left top, right top, from(@startColor), to(@endColor)); // Konqueror 73 | background-image: -ms-linear-gradient(left, @startColor, @endColor); // IE10 74 | background-image: -webkit-gradient(linear, left top, right top, color-stop(0%, @startColor), color-stop(100%, @endColor)); // Safari 4+, Chrome 2+ 75 | background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+ 76 | background-image: linear-gradient(left, @startColor, @endColor); // Le standard 77 | } 78 | 79 | .directional-gradient (@startColor: #555, @endColor: #333, @deg: 45deg) { 80 | background-color: @endColor; 81 | background-repeat: repeat-x; 82 | filter: ~"progid:DXImageTransform.Microsoft.gradient(startColorstr='@{startColor}', endColorstr='@{endColor}',GradientType=0)"; // IE6 - 9 83 | background-image: -ms-linear-gradient(@deg, @startColor, @endColor); // IE10 84 | background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+ 85 | background-image: linear-gradient(@deg, @startColor, @endColor); // The standard 86 | } 87 | 88 | .vertical-gradient-3 (@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { 89 | background-color: @endColor; 90 | background-repeat: no-repeat; 91 | filter: ~"progid:DXImageTransform.Microsoft.gradient(startColorstr='@{startColor}', endColorstr='@{endColor}',GradientType=0)"; // IE6 - 9 92 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); 93 | background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor); 94 | background-image: -ms-linear-gradient(@startColor, @midColor @colorStop, @endColor); 95 | background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor); 96 | } 97 | 98 | //Text Selection 99 | .text-select(@enabled: text) { 100 | -webkit-user-select: @enabled; 101 | -ms-user-select:@enabled; 102 | user-select:@enabled; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /client/app/styles/less/components/modals.less: -------------------------------------------------------------------------------- 1 | .modal { 2 | position: fixed; 3 | left: 0; 4 | top: 0; 5 | right: 0; 6 | bottom: 0; 7 | z-index: 100; 8 | display: table; 9 | height: 100%; 10 | width: 100%; 11 | color: @text-color-on-white; 12 | font-weight: 400; 13 | 14 | a { 15 | color: @link-color-on-white; 16 | } 17 | } 18 | 19 | .modal-overlay { 20 | margin: 0 auto; 21 | display: table-cell; 22 | vertical-align: middle; 23 | padding: 0 40px; 24 | 25 | .content { 26 | background-color: @light-bg-color; 27 | width: 100%; 28 | max-width: 900px; 29 | margin: 0 auto; 30 | padding-right: 335px; 31 | position: relative; 32 | .box-shadow(@shadow: 0 2px 6px rgba(0,0,0,.9)) 33 | } 34 | 35 | .no-media { 36 | max-width: 335px; 37 | padding: 0; 38 | 39 | .details { 40 | position: relative; 41 | } 42 | } 43 | 44 | .no-details { 45 | max-width: 565px; 46 | padding-right: 0; 47 | } 48 | 49 | .media { 50 | position: relative; 51 | height: 100%; 52 | 53 | img, video { 54 | width: 100%; 55 | height: 100%; 56 | display: block; 57 | } 58 | 59 | .icon.play { 60 | position: absolute; 61 | z-index: 50; 62 | top: 50%; 63 | left: 50%; 64 | height: 80px; 65 | width: 80px; 66 | line-height: 80px; 67 | font-size: 80px; 68 | margin-left: -40px; 69 | margin-top: -40px; 70 | cursor: pointer; 71 | .transition(e('all 320ms ease-out')); 72 | } 73 | 74 | .icon.right { 75 | position: absolute; 76 | z-index: 50; 77 | top: 50%; 78 | right: 5%; 79 | margin-left: -15px; 80 | margin-top: -15px; 81 | cursor: pointer; 82 | color: #fff; 83 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); 84 | .transition(e('all 200ms ease-out')); 85 | 86 | &:hover { 87 | .transform(@transform:scale(1.1)); 88 | } 89 | } 90 | 91 | .icon.left { 92 | position: absolute; 93 | z-index: 50; 94 | top: 50%; 95 | left: 5%; 96 | margin-left: -15px; 97 | margin-top: -15px; 98 | cursor: pointer; 99 | color: #fff; 100 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); 101 | .transition(e('all 200ms ease-out')); 102 | 103 | &:hover { 104 | .transform(@transform:scale(1.1)); 105 | } 106 | } 107 | } 108 | 109 | .details { 110 | position: absolute; 111 | width: 335px; 112 | padding: 20px; 113 | top: 0; 114 | right: 0; 115 | overflow: scroll; 116 | height: 100%; 117 | } 118 | 119 | .user-info { 120 | display: block; 121 | padding: 0 0 15px; 122 | height: 50px; 123 | color: @text-color-on-white; 124 | .clearfix; 125 | 126 | .picture { 127 | width: 40px; 128 | height: 40px; 129 | .border-radius(20px); 130 | overflow: hidden; 131 | display: inline-block; 132 | border: 1px solid #ccc; 133 | float:left; 134 | 135 | img { 136 | width: 40px; 137 | height: 40px; 138 | } 139 | } 140 | 141 | h4 { 142 | float: left; 143 | display: inline-block; 144 | vertical-align: top; 145 | line-height: 40px; 146 | padding-left: 10px; 147 | 148 | &.multiline { 149 | line-height: 20px; 150 | } 151 | } 152 | 153 | h4 strong { 154 | color: @text-color-on-white; 155 | font-weight: 600; 156 | display: block; 157 | } 158 | } 159 | 160 | .stats { 161 | padding:10px 0; 162 | 163 | a { 164 | color: @text-color-on-white; 165 | display: inline-block; 166 | margin-right: 20px; 167 | } 168 | 169 | .small-icon { 170 | color: @text-color-on-white; 171 | font-size: 14px; 172 | height: 24px; 173 | line-height: 24px; 174 | } 175 | 176 | .val { 177 | font-weight: 500; 178 | margin-left: 5px; 179 | } 180 | } 181 | 182 | .date { 183 | font-size: 12px; 184 | line-height: 20px; 185 | display: block; 186 | color: @alt-text-color-on-white; 187 | } 188 | } 189 | 190 | .modal-background { 191 | position: absolute; 192 | left: 0; 193 | top: 0; 194 | background-color: #000; 195 | opacity: 0.4; 196 | width: 100%; 197 | height: 100%; 198 | z-index: -1; 199 | } 200 | 201 | .modal-overlay .youtube { 202 | iframe { 203 | display: block; 204 | } 205 | 206 | .title { 207 | margin-bottom: 10px; 208 | } 209 | 210 | .date { 211 | margin-bottom: 10px; 212 | } 213 | } 214 | 215 | .close-btn { 216 | background: transparent; 217 | width: 30px; 218 | height: 30px; 219 | display: inline-block; 220 | color: @light-bg-color; 221 | position: absolute; 222 | top:0; 223 | right: 0; 224 | font-size: 30px; 225 | line-height: 30px; 226 | 227 | &:before { 228 | content: "\00d7"; 229 | } 230 | } 231 | 232 | .modal.ng-enter { 233 | transition: opacity 350ms ease-out; 234 | opacity: 0; 235 | } 236 | 237 | .modal.ng-enter.ng-enter-active { 238 | opacity: 1; 239 | } 240 | 241 | .modal.ng-leave { 242 | transition: opacity 350ms ease-out; 243 | opacity: 1; 244 | } 245 | 246 | .modal.ng-leave.ng-leave-active { 247 | opacity: 0; 248 | } 249 | 250 | @media (max-width: 960px) { 251 | .modal-overlay { 252 | .content.youtube { 253 | padding-right: 0; 254 | width: 560px; 255 | 256 | .details { 257 | position: relative; 258 | width: 100%; 259 | } 260 | } 261 | } 262 | } 263 | 264 | @media (max-width: 800px) { 265 | .modal-overlay { 266 | .content { 267 | padding-right: 0; 268 | max-width: 500px; 269 | } 270 | 271 | .details { 272 | position: relative; 273 | width: 100%; 274 | } 275 | } 276 | } 277 | 278 | @media (max-width: 600px) { 279 | .modal-overlay { 280 | .content.youtube { 281 | padding-right: 0; 282 | width: 100%; 283 | 284 | iframe { 285 | width: 100%; 286 | } 287 | } 288 | } 289 | } -------------------------------------------------------------------------------- /client/app/styles/less/components/reset.less: -------------------------------------------------------------------------------- 1 | *, *:before, *:after {-moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } 2 | html, body {margin: 0; padding: 0; height: 100%;} 3 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, cite, code, del, dfn, em, img, q, s, samp, small, strike, strong, sub, sup, tt, var, dd, dl, dt, li, ol, ul, fieldset, form, label, legend, button, table, caption, tbody, tfoot, thead, tr, th, td {margin: 0; padding: 0; border: 0; font-style: normal; font-size: 100%; font-family: inherit;} 4 | strong {font-weight: bold;} 5 | body {line-height: 1;} 6 | table {border-collapse: collapse; border-spacing: 0;} 7 | ol, ul {list-style: none;} 8 | q:before, q:after, blockquote:before, blockquote:after {content: "";} 9 | html {overflow-y: scroll; font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%;} 10 | *:focus {outline: none;} 11 | article, aside, details, figcaption, figure, footer, header, hgroup, nav, section {display: block;} 12 | audio, canvas, video {display: inline-block; *display: inline; *zoom: 1;} 13 | audio:not([controls]) {display: none;} 14 | sub, sup {font-size: 75%; line-height: 0; position: relative; vertical-align: baseline;} 15 | sup {top: -0.5em;} 16 | sub {bottom: -0.25em;} 17 | img {border: none; -ms-interpolation-mode: bicubic;} 18 | button, input, select, textarea {font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle;} 19 | button, input {line-height: normal; *overflow: visible;} 20 | button::-moz-focus-inner, input::-moz-focus-inner {border: 0;padding: 0;} 21 | button, input[type="button"], input[type="reset"], input[type="submit"] {cursor: pointer; -webkit-appearance: button;} 22 | textarea {overflow: auto; vertical-align: top;} -------------------------------------------------------------------------------- /client/app/styles/less/pages/dribbble.less: -------------------------------------------------------------------------------- 1 | .dribbble-page { 2 | position: relative; 3 | opacity: 0; 4 | .clearfix; 5 | 6 | .user { 7 | position: relative; 8 | padding: 40px 0 15px 0; 9 | .clearfix; 10 | 11 | .picture { 12 | width: 60px; 13 | height: 60px; 14 | .border-radius(40px); 15 | overflow: hidden; 16 | display: inline-block; 17 | .box-shadow(@shadow: 0 2px 6px rgba(0,0,0,.9)); 18 | z-index: 2; 19 | float: left; 20 | 21 | img { 22 | width: 60px; 23 | height: 60px; 24 | } 25 | } 26 | 27 | .name { 28 | float: left; 29 | margin-left: 20px; 30 | margin-top: 5px; 31 | 32 | h2, .username { 33 | position: relative; 34 | } 35 | } 36 | 37 | .stats { 38 | float: left; 39 | .clearfix; 40 | .border-radius(4px); 41 | overflow: hidden; 42 | background-color: @dark-bg-color; 43 | margin-left: 70px; 44 | width: 300px; 45 | 46 | li { 47 | width: 33%; 48 | float: left; 49 | text-align: center; 50 | padding: 8px 0; 51 | font-size: 12px; 52 | line-height: 20px; 53 | } 54 | 55 | strong { 56 | font-weight: 300; 57 | color: @link-color; 58 | display: block; 59 | font-size: 16px; 60 | line-height: 26px; 61 | } 62 | } 63 | } 64 | 65 | .bio { 66 | position: relative; 67 | } 68 | 69 | .posts { 70 | .clearfix; 71 | margin-top: 30px; 72 | margin-left: -30px; 73 | 74 | .post { 75 | width: 28%; 76 | float: left; 77 | margin: 0 0 30px 30px; 78 | position: relative; 79 | .transition(e('all 320ms ease-out')); 80 | 81 | &:hover { 82 | .transform(@transform:scale(1.02)); 83 | 84 | .stats { 85 | opacity: 1.0; 86 | } 87 | } 88 | 89 | img { 90 | width: 100%; 91 | cursor: pointer; 92 | display: block; 93 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 94 | } 95 | } 96 | 97 | .stats { 98 | text-align: center; 99 | width: 100%; 100 | height: 100%; 101 | position: absolute; 102 | top: 0; 103 | background-color: rgba(0, 0, 0, 0.8); 104 | pointer-events:none; 105 | z-index: 2; 106 | opacity: 0; 107 | .transition(e('all 320ms ease-out')); 108 | 109 | .small-icon { 110 | font-size: 14px; 111 | height: 24px; 112 | line-height: 24px; 113 | color: @text-color; 114 | position: relative; 115 | display: inline-block; 116 | top: 50%; 117 | margin-top: -10px; 118 | } 119 | 120 | .val { 121 | font-weight: 500; 122 | margin-left: 5px; 123 | margin-right: 10px; 124 | color: @text-color; 125 | position: relative; 126 | display: inline-block; 127 | top: 50%; 128 | margin-top: -10px; 129 | vertical-align: top; 130 | } 131 | } 132 | } 133 | } 134 | 135 | .dribbble-page.visible { 136 | opacity: 1; 137 | } 138 | 139 | .dribbble-page.animate-enter { 140 | .animation(e('fade-in 200ms ease-in backwards')); 141 | 142 | .user { 143 | .picture { 144 | .animation(e('scale-in 350ms')); 145 | } 146 | 147 | h2 { 148 | .animation(e('move-in 500ms')); 149 | } 150 | 151 | .username { 152 | .animation(e('move-in 700ms')); 153 | } 154 | 155 | .stats li { 156 | .animation(e('scale-in 500ms')); 157 | } 158 | } 159 | 160 | p { 161 | .animation(e('move-in 600ms')); 162 | } 163 | 164 | .posts .pic { 165 | .animation(e('half-scale-in 500ms')); 166 | } 167 | } 168 | 169 | .main-view.ng-leave .dribbble-page { 170 | .user { 171 | .picture { 172 | .animation(e('scale-in 400ms reverse')); 173 | } 174 | 175 | h2 { 176 | .animation(e('move-out 500ms')); 177 | } 178 | 179 | .username { 180 | .animation(e('move-out 700ms')); 181 | } 182 | 183 | .stats li { 184 | .animation(e('scale-in 700ms reverse')); 185 | } 186 | } 187 | 188 | p { 189 | .animation(e('move-out 600ms')); 190 | } 191 | 192 | .posts .pic { 193 | .animation(e('half-scale-in 500ms reverse')); 194 | } 195 | } 196 | 197 | @media (max-width: 700px) { 198 | .dribbble-page { 199 | .user .stats { 200 | float: none; 201 | margin-left: 0; 202 | clear: both; 203 | position: relative;; 204 | top: 20px; 205 | margin-bottom: 15px; 206 | width: 100%; 207 | } 208 | 209 | .posts { 210 | margin-left: -5%; 211 | 212 | .post { 213 | width: 45%; 214 | margin: 0 0 15px 5%; 215 | } 216 | } 217 | } 218 | } 219 | 220 | @media (max-width: 480px) { 221 | .dribbble-page .posts { 222 | margin-top: 0; 223 | } 224 | } -------------------------------------------------------------------------------- /client/app/styles/less/pages/github.less: -------------------------------------------------------------------------------- 1 | .github-page { 2 | position: relative; 3 | opacity: 0; 4 | .clearfix; 5 | 6 | .user { 7 | position: relative; 8 | float: left; 9 | width: 35%; 10 | padding: 40px 0; 11 | 12 | .picture { 13 | width: 60px; 14 | height: 60px; 15 | .border-radius(40px); 16 | overflow: hidden; 17 | display: inline-block; 18 | .box-shadow(@shadow: 0 2px 6px rgba(0,0,0,.9)); 19 | position: absolute; 20 | z-index: 1; 21 | 22 | img { 23 | width: 60px; 24 | height: 60px; 25 | } 26 | } 27 | 28 | h2 { 29 | margin-left: 80px; 30 | margin-top: 5px; 31 | position: relative; 32 | } 33 | 34 | .username { 35 | margin-left: 80px; 36 | margin-bottom: 30px; 37 | display: block; 38 | position: relative; 39 | } 40 | 41 | h3 { 42 | position: relative; 43 | margin-bottom: 10px; 44 | } 45 | 46 | .stats { 47 | .clearfix; 48 | .border-radius(4px); 49 | overflow: hidden; 50 | background-color: @dark-bg-color; 51 | margin-bottom: 30px; 52 | 53 | li { 54 | width: 33%; 55 | float: left; 56 | text-align: center; 57 | padding: 10px 0; 58 | font-size: 12px; 59 | line-height: 20px; 60 | } 61 | 62 | strong { 63 | font-weight: 300; 64 | color: @link-color; 65 | display: block; 66 | font-size: 16px; 67 | line-height: 26px; 68 | } 69 | } 70 | 71 | } 72 | 73 | .repos { 74 | 75 | li.repo { 76 | border-top: 1px solid @line-color; 77 | padding: 15px 0; 78 | position: relative; 79 | } 80 | 81 | a { 82 | font-weight: 400; 83 | position: relative; 84 | } 85 | 86 | p { 87 | position: relative; 88 | margin: 0; 89 | padding: 10px 0; 90 | } 91 | 92 | .repo-stats { 93 | position: relative; 94 | .clearfix; 95 | 96 | li { 97 | display: inline-block; 98 | float: left; 99 | margin-right: 10px; 100 | color: @alt-text-color; 101 | font-weight: 400; 102 | } 103 | 104 | .small-icon { 105 | font-size: 14px; 106 | line-height: 24px; 107 | height: 24px; 108 | margin-right: 5px; 109 | color: @alt-text-color; 110 | } 111 | 112 | } 113 | } 114 | 115 | .activities { 116 | float: left; 117 | width: 65%; 118 | padding: 40px 0 40px 40px; 119 | 120 | ul, h2 { 121 | display: block; 122 | } 123 | 124 | h2 { 125 | margin-bottom: 10px; 126 | } 127 | 128 | li { 129 | border-top: 1px solid @line-color; 130 | padding: 15px 0; 131 | position: relative; 132 | } 133 | 134 | a { 135 | font-weight: 400; 136 | position: relative; 137 | } 138 | 139 | p { 140 | position: relative; 141 | margin: 0; 142 | padding: 0; 143 | } 144 | 145 | .icon { 146 | color: @alt-text-color; 147 | position: absolute; 148 | } 149 | 150 | .date { 151 | position: relative; 152 | color: @alt-text-color; 153 | font-size: 12px; 154 | margin-left: 40px; 155 | font-weight: 400; 156 | } 157 | 158 | .description { 159 | position: relative; 160 | margin: 2px 0 5px 40px; 161 | } 162 | 163 | div.comment { 164 | position: relative; 165 | opacity: 0.55; 166 | margin-left: 40px; 167 | margin-bottom: 5px; 168 | } 169 | 170 | } 171 | } 172 | 173 | .github-page.visible { 174 | opacity: 1; 175 | } 176 | 177 | .github-page.animate-enter { 178 | .animation(e('fade-in 200ms ease-in backwards')); 179 | 180 | .user { 181 | .picture { 182 | .animation(e('scale-in 350ms')); 183 | } 184 | 185 | h2, .username { 186 | .animation(e('move-in 500ms')); 187 | } 188 | 189 | .repo a { 190 | .animation(e('move-in 400ms')); 191 | } 192 | 193 | p { 194 | .animation(e('move-in 600ms')); 195 | } 196 | 197 | .repo-stats { 198 | .animation(e('move-in 800ms')); 199 | } 200 | 201 | .stats li { 202 | .animation(e('scale-in 500ms')); 203 | } 204 | } 205 | 206 | .activities { 207 | .icon { 208 | .animation(e('scale-in 500ms')); 209 | } 210 | 211 | .description { 212 | .animation(e('move-in 500ms')); 213 | } 214 | 215 | div.comment { 216 | .animation(e('move-in 500ms')); 217 | } 218 | 219 | .date { 220 | .animation(e('move-in 600ms')); 221 | } 222 | } 223 | } 224 | 225 | .main-view.ng-leave .github-page { 226 | .user { 227 | .picture { 228 | .animation(e('scale-in 400ms reverse')); 229 | } 230 | 231 | .stats li { 232 | .animation(e('scale-in 700ms reverse')); 233 | } 234 | 235 | p { 236 | .animation(e('move-out 700ms')); 237 | } 238 | } 239 | 240 | .activities { 241 | .description { 242 | .animation(e('move-out 700ms')); 243 | } 244 | } 245 | } 246 | 247 | @media (max-width: 900px) { 248 | .github-page { 249 | .user { 250 | width: 40%; 251 | } 252 | 253 | .activities { 254 | width: 60%; 255 | } 256 | } 257 | } 258 | 259 | @media (max-width: 700px) { 260 | .github-page { 261 | .user { 262 | float: none; 263 | width: 100%; 264 | padding-bottom: 0; 265 | } 266 | 267 | .repos { 268 | margin-bottom: 20px; 269 | } 270 | 271 | .activities { 272 | float: none; 273 | width: 100%; 274 | padding: 0; 275 | } 276 | } 277 | } -------------------------------------------------------------------------------- /client/app/styles/less/pages/instagram.less: -------------------------------------------------------------------------------- 1 | .instagram-page { 2 | position: relative; 3 | opacity: 0; 4 | .clearfix; 5 | 6 | .user { 7 | position: relative; 8 | padding: 40px 0 15px 0; 9 | .clearfix; 10 | 11 | .picture { 12 | width: 60px; 13 | height: 60px; 14 | .border-radius(40px); 15 | overflow: hidden; 16 | display: inline-block; 17 | .box-shadow(@shadow: 0 2px 6px rgba(0,0,0,.9)); 18 | z-index: 2; 19 | float: left; 20 | 21 | img { 22 | width: 60px; 23 | height: 60px; 24 | } 25 | } 26 | 27 | .name { 28 | float: left; 29 | margin-left: 20px; 30 | margin-top: 5px; 31 | 32 | h2, .username { 33 | position: relative; 34 | } 35 | } 36 | 37 | .stats { 38 | float: left; 39 | .clearfix; 40 | .border-radius(4px); 41 | overflow: hidden; 42 | background-color: @dark-bg-color; 43 | margin-left: 70px; 44 | width: 300px; 45 | 46 | li { 47 | width: 33%; 48 | float: left; 49 | text-align: center; 50 | padding: 8px 0; 51 | font-size: 12px; 52 | line-height: 20px; 53 | } 54 | 55 | strong { 56 | font-weight: 300; 57 | color: @link-color; 58 | display: block; 59 | font-size: 16px; 60 | line-height: 26px; 61 | } 62 | } 63 | } 64 | 65 | .bio { 66 | position: relative; 67 | } 68 | 69 | .posts { 70 | .clearfix; 71 | margin-top: 30px; 72 | margin-left: -30px; 73 | 74 | .post { 75 | width: 21%; 76 | height: 0; 77 | padding-bottom: 21%; 78 | overflow: hidden; 79 | float: left; 80 | margin: 0 0 30px 30px; 81 | position: relative; 82 | max-width: 250px; 83 | .transition(e('all 320ms ease-out')); 84 | 85 | &:hover { 86 | .transform(@transform:scale(1.02)); 87 | 88 | .stats { 89 | opacity: 1.0; 90 | } 91 | } 92 | 93 | img { 94 | width: 100%; 95 | cursor: pointer; 96 | display: block; 97 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 98 | } 99 | 100 | .icon.play-video { 101 | position: absolute; 102 | z-index: 1; 103 | left: 50%; 104 | top: 50%; 105 | margin-left: -30px; 106 | margin-top: -30px; 107 | pointer-events:none; 108 | 109 | font-size: 60px; 110 | height: 60px; 111 | width: 60px; 112 | line-height: 60px; 113 | } 114 | } 115 | 116 | .stats { 117 | text-align: center; 118 | width: 100%; 119 | height: 100%; 120 | position: absolute; 121 | top: 0; 122 | background-color: rgba(0, 0, 0, 0.8); 123 | pointer-events:none; 124 | z-index: 2; 125 | opacity: 0; 126 | .transition(e('all 320ms ease-out')); 127 | 128 | .small-icon { 129 | font-size: 14px; 130 | height: 24px; 131 | line-height: 24px; 132 | color: @text-color; 133 | position: relative; 134 | display: inline-block; 135 | top: 50%; 136 | margin-top: -10px; 137 | } 138 | 139 | .val { 140 | font-weight: 500; 141 | margin-left: 5px; 142 | margin-right: 10px; 143 | color: @text-color; 144 | position: relative; 145 | display: inline-block; 146 | top: 50%; 147 | margin-top: -10px; 148 | vertical-align: top; 149 | } 150 | } 151 | } 152 | } 153 | 154 | .instagram-page.visible { 155 | opacity: 1; 156 | } 157 | 158 | .instagram-page.animate-enter { 159 | .animation(e('fade-in 200ms ease-in backwards')); 160 | 161 | .user { 162 | .picture { 163 | .animation(e('scale-in 350ms')); 164 | } 165 | 166 | h2 { 167 | .animation(e('move-in 500ms')); 168 | } 169 | 170 | .username { 171 | .animation(e('move-in 700ms')); 172 | } 173 | 174 | .stats li { 175 | .animation(e('scale-in 500ms')); 176 | } 177 | } 178 | 179 | p { 180 | .animation(e('move-in 600ms')); 181 | } 182 | 183 | .posts .pic { 184 | .animation(e('half-scale-in 500ms')); 185 | } 186 | } 187 | 188 | .main-view.ng-leave .instagram-page { 189 | .user { 190 | .picture { 191 | .animation(e('scale-in 400ms reverse')); 192 | } 193 | 194 | h2 { 195 | .animation(e('move-out 500ms')); 196 | } 197 | 198 | .username { 199 | .animation(e('move-out 700ms')); 200 | } 201 | 202 | .stats li { 203 | .animation(e('scale-in 700ms reverse')); 204 | } 205 | } 206 | 207 | p { 208 | .animation(e('move-out 600ms')); 209 | } 210 | 211 | .posts .pic { 212 | .animation(e('half-scale-in 500ms reverse')); 213 | } 214 | } 215 | 216 | @media (max-width: 900px) { 217 | .instagram-page .posts .post { 218 | width: 26%; 219 | padding-bottom: 26%; 220 | } 221 | } 222 | 223 | @media (max-width: 750px) { 224 | .instagram-page .user .stats { 225 | float: none; 226 | margin-left: 0; 227 | clear: both; 228 | position: relative;; 229 | top: 20px; 230 | } 231 | } 232 | 233 | @media (max-width: 700px) { 234 | .instagram-page { 235 | .user .stats { 236 | margin-bottom: 15px; 237 | width: 100%; 238 | } 239 | 240 | .posts { 241 | margin-top: 10px; 242 | margin-left: -5%; 243 | 244 | .post { 245 | width: 45%; 246 | max-width: 45%; 247 | padding-bottom: 45%; 248 | margin: 0 0 20px 5%; 249 | } 250 | } 251 | } 252 | } 253 | 254 | @media (max-width: 480px) { 255 | .instagram-page .posts { 256 | margin-top: 5px; 257 | } 258 | } 259 | 260 | -------------------------------------------------------------------------------- /client/app/styles/less/pages/lastfm.less: -------------------------------------------------------------------------------- 1 | .lastfm-page { 2 | position: relative; 3 | opacity: 0; 4 | .clearfix; 5 | 6 | .user { 7 | position: relative; 8 | float: left; 9 | width: 35%; 10 | padding: 40px 0; 11 | 12 | .picture { 13 | width: 60px; 14 | height: 60px; 15 | .border-radius(40px); 16 | overflow: hidden; 17 | display: inline-block; 18 | .box-shadow(@shadow: 0 2px 6px rgba(0,0,0,.9)); 19 | position: absolute; 20 | z-index: 1; 21 | 22 | img { 23 | width: 60px; 24 | height: 60px; 25 | } 26 | } 27 | 28 | h2 { 29 | margin-left: 80px; 30 | margin-top: 5px; 31 | position: relative; 32 | } 33 | 34 | p { 35 | position: relative; 36 | padding: 10px 0; 37 | } 38 | 39 | .username { 40 | margin-left: 80px; 41 | margin-bottom: 30px; 42 | display: block; 43 | position: relative; 44 | } 45 | 46 | h3 { 47 | position: relative; 48 | margin-bottom: 10px; 49 | } 50 | } 51 | 52 | .tracks { 53 | position: relative; 54 | 55 | li.track { 56 | border-top: 1px solid @line-color; 57 | padding: 15px 0; 58 | position: relative; 59 | } 60 | 61 | .track-image { 62 | position: absolute; 63 | width: 48px; 64 | height: 48px; 65 | background-color: @dark-bg-color; 66 | .border-radius(5px); 67 | text-align: center; 68 | } 69 | 70 | img { 71 | width: 100%; 72 | height: 100%; 73 | .border-radius(5px); 74 | display: block; 75 | } 76 | 77 | .icon { 78 | width: 32px; 79 | height: 32px; 80 | margin-top: 8px; 81 | opacity: 0.1; 82 | font-size: 20px; 83 | text-indent: -2px; 84 | } 85 | 86 | .track-info { 87 | margin-left: 60px; 88 | position: relative; 89 | } 90 | 91 | .date { 92 | color: @alt-text-color; 93 | font-size: 12px; 94 | } 95 | } 96 | 97 | .top-stats { 98 | float: left; 99 | width: 63%; 100 | padding: 40px 0 40px 40px; 101 | position: relative; 102 | 103 | h2 { 104 | margin-bottom: 10px; 105 | position: relative; 106 | } 107 | 108 | .top { 109 | .clearfix; 110 | margin-bottom: 30px; 111 | 112 | li { 113 | position: relative; 114 | float: left; 115 | width: 125px; 116 | height: 125px; 117 | cursor: pointer; 118 | } 119 | 120 | li:first-child { 121 | width: 250px; 122 | height: 250px; 123 | } 124 | 125 | .image { 126 | width: 100%; 127 | height: 100%; 128 | background-color: rgba(0, 0, 0, 0.2); 129 | background-size: cover; 130 | background-position: 50% 50%; 131 | } 132 | 133 | .info { 134 | position: absolute; 135 | z-index: 1; 136 | bottom: 0; 137 | width: 100%; 138 | padding: 100px 10px 10px 10px; 139 | background: rgba(0,0,0,0.5); 140 | background: -moz-linear-gradient(top, rgba(0,0,0,0) 20%, rgba(0,0,0,0.8) 100%); 141 | background: -webkit-linear-gradient(top, rgba(0,0,0,0) 20%, rgba(0,0,0,0.8) 100%); 142 | background: linear-gradient(to bottom, rgba(0,0,0,0) 20%, rgba(0,0,0,0.8) 100%); 143 | 144 | a { 145 | color: @text-color; 146 | font-weight: 400; 147 | display: block; 148 | width: 100%; 149 | overflow: hidden; 150 | text-overflow: ellipsis; 151 | white-space: nowrap; 152 | } 153 | 154 | .plays, .artist { 155 | font-size: 12px; 156 | line-height: 20px; 157 | } 158 | } 159 | } 160 | 161 | } 162 | } 163 | 164 | .lastfm-page.visible { 165 | opacity: 1; 166 | } 167 | 168 | .lastfm-page.animate-enter { 169 | .animation(e('fade-in 200ms ease-in backwards')); 170 | 171 | .user { 172 | .picture { 173 | .animation(e('scale-in 350ms')); 174 | } 175 | 176 | h2, .username { 177 | .animation(e('move-in 500ms')); 178 | } 179 | } 180 | 181 | .track-image { 182 | .animation(e('scale-in 500ms')); 183 | } 184 | 185 | .track-info, .top-stats h2 { 186 | .animation(e('move-in 600ms')); 187 | } 188 | 189 | .top-stats li { 190 | .animation(e('half-scale-in 500ms')); 191 | } 192 | } 193 | 194 | .main-view.ng-leave .lastfm-page { 195 | 196 | .user { 197 | .picture { 198 | .animation(e('scale-in 400ms reverse')); 199 | } 200 | } 201 | 202 | .track-info, .top-stats h2 { 203 | .animation(e('move-out 600ms')); 204 | } 205 | 206 | .top-stats li { 207 | .animation(e('half-scale-in 500ms reverse')); 208 | } 209 | } 210 | 211 | @media (max-width: 900px) { 212 | .lastfm-page { 213 | .user { 214 | width: 43%; 215 | } 216 | 217 | .top-stats { 218 | width: 57%; 219 | } 220 | } 221 | } 222 | 223 | @media (max-width: 700px) { 224 | .lastfm-page { 225 | .user { 226 | float: none; 227 | width: 100%; 228 | padding-bottom: 0; 229 | } 230 | 231 | .tracks { 232 | margin-bottom: 20px; 233 | li.track { 234 | display: none; 235 | } 236 | 237 | li.track:nth-child(-n+10) { 238 | display: block; 239 | } 240 | } 241 | 242 | .top-stats { 243 | float: none; 244 | width: 100%; 245 | padding: 0; 246 | } 247 | } 248 | } -------------------------------------------------------------------------------- /client/app/styles/less/pages/stream.less: -------------------------------------------------------------------------------- 1 | .stream { 2 | position: relative; 3 | margin-left: 1%; 4 | padding: 40px 0; 5 | opacity: 0; 6 | 7 | .stream-line { 8 | height: 100%; 9 | width: 4px; 10 | background-color: @line-color; 11 | position: fixed; 12 | z-index: 0; 13 | top: 0; 14 | margin-left: 100px; 15 | } 16 | 17 | li { 18 | .clearfix; 19 | position: relative; 20 | } 21 | 22 | h2 { 23 | margin-left: 100px; 24 | padding: 10px 40px 30px 45px; 25 | position: relative; 26 | } 27 | 28 | h3 { 29 | float: left; 30 | width: 100px; 31 | color: @alt-text-color; 32 | margin-top: 11px; 33 | } 34 | 35 | .small-icon { 36 | position: absolute; 37 | z-index: 1; 38 | color: @alt-text-color; 39 | font-size: 14px; 40 | background-color: @line-color; 41 | width: 40px; 42 | height: 40px; 43 | line-height: 40px; 44 | .border-radius(20px); 45 | left: 82px; 46 | top: 4px; 47 | } 48 | 49 | .content { 50 | position: relative; 51 | float:left; 52 | max-width: 80%; 53 | padding: 0 40px 40px 45px; 54 | } 55 | 56 | .twitter { 57 | .pictures { 58 | display: inline-block; 59 | margin-right: 5px; 60 | position: relative; 61 | 62 | &.multiple .pic { 63 | width: 73px; 64 | height: 73px; 65 | } 66 | } 67 | 68 | .content { 69 | padding-top: 11px; 70 | } 71 | 72 | .pic { 73 | width: 150px; 74 | height: 150px; 75 | display: block; 76 | background-size: cover; 77 | background-position: 50% 50%; 78 | cursor: pointer; 79 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 80 | .transition(e('all 320ms ease-out')); 81 | 82 | &:hover { 83 | .transform(@transform:scale(1.07)); 84 | } 85 | } 86 | 87 | .icon.play-video { 88 | position: absolute; 89 | z-index: 1; 90 | left: 50px; 91 | top: 50px; 92 | pointer-events: none; 93 | } 94 | } 95 | 96 | .instagram { 97 | .pic { 98 | width: 150px; 99 | height: 150px; 100 | display: block; 101 | background-size: cover; 102 | background-position: 50% 50%; 103 | cursor: pointer; 104 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 105 | .transition(e('all 320ms ease-out')); 106 | 107 | &:hover { 108 | .transform(@transform:scale(1.07)); 109 | } 110 | } 111 | 112 | .content { 113 | padding-top: 6px; 114 | } 115 | 116 | .icon.play-video { 117 | position: absolute; 118 | z-index: 1; 119 | left: 95px; 120 | top: 55px; 121 | pointer-events: none; 122 | } 123 | } 124 | 125 | .foursquare { 126 | 127 | .checkin-icon { 128 | position: absolute; 129 | width: 48px; 130 | height: 48px; 131 | background-color: @dark-bg-color; 132 | .border-radius(5px); 133 | text-align: center; 134 | margin-right: 15px; 135 | } 136 | 137 | img { 138 | width: 32px; 139 | height: 32px; 140 | margin-top: 8px; 141 | opacity: 0.7; 142 | } 143 | 144 | .info { 145 | margin-left: 60px; 146 | margin-bottom: 15px; 147 | } 148 | 149 | .alt { 150 | opacity: 0.55; 151 | } 152 | } 153 | 154 | .dribbble { 155 | .pic { 156 | width: 200px; 157 | height: 150px; 158 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 159 | cursor: pointer; 160 | .transition(e('all 320ms ease-out')); 161 | 162 | &:hover { 163 | .transform(@transform:scale(1.07)); 164 | } 165 | } 166 | 167 | .content { 168 | padding-top: 6px; 169 | } 170 | } 171 | 172 | .tumblr { 173 | .pictures { 174 | display: inline-block; 175 | margin-right: 5px; 176 | margin-bottom: 10px; 177 | position: relative; 178 | 179 | &.multiple .pic, &.multiple .link-pic { 180 | width: 73px; 181 | height: 73px; 182 | } 183 | } 184 | 185 | .content { 186 | padding-top: 11px; 187 | } 188 | 189 | .pic { 190 | width: 150px; 191 | height: 150px; 192 | display: block; 193 | background-size: cover; 194 | background-position: 50% 50%; 195 | cursor: pointer; 196 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 197 | .transition(e('all 320ms ease-out')); 198 | 199 | &:hover { 200 | .transform(@transform:scale(1.07)); 201 | } 202 | } 203 | 204 | .link-pic { 205 | width: 150px; 206 | height: 150px; 207 | display: block; 208 | background-size: cover; 209 | background-position: 50% 50%; 210 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 211 | } 212 | 213 | .player { 214 | margin-bottom: 15px; 215 | 216 | iframe { 217 | display: block; 218 | } 219 | } 220 | } 221 | 222 | .github .content { 223 | padding-top: 11px; 224 | } 225 | 226 | .lastfm { 227 | .pictures { 228 | display: inline-block; 229 | margin-right: 5px; 230 | position: relative; 231 | } 232 | 233 | .content { 234 | padding-top: 11px; 235 | } 236 | 237 | .pic { 238 | width: 40px; 239 | height: 40px; 240 | display: block; 241 | background-size: cover; 242 | background-position: 50% 50%; 243 | cursor: pointer; 244 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 245 | .transition(e('all 320ms ease-out')); 246 | 247 | &:hover { 248 | .transform(@transform:scale(1.1)); 249 | } 250 | } 251 | } 252 | 253 | .youtube { 254 | .pic { 255 | width: 250px; 256 | height: 141px; 257 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 258 | cursor: pointer; 259 | .transition(e('all 320ms ease-out')); 260 | 261 | &:hover { 262 | .transform(@transform:scale(1.07)); 263 | } 264 | } 265 | 266 | .content { 267 | padding-top: 6px; 268 | } 269 | 270 | .icon.play-video { 271 | position: absolute; 272 | z-index: 1; 273 | left: 145px; 274 | top: 50px; 275 | pointer-events: none; 276 | } 277 | } 278 | } 279 | 280 | .stream.visible { 281 | opacity: 1.0; 282 | } 283 | 284 | .stream.animate-enter { 285 | .animation(e('fade-in 200ms ease-in backwards')); 286 | 287 | .stream-line { 288 | .animation(e('scale-in 500ms ease-in 50ms backwards')); 289 | } 290 | 291 | .stream-group:nth-child(-n+5) { 292 | 293 | .small-icon { 294 | .animation(e('scale-in 350ms')); 295 | } 296 | 297 | .day-title { 298 | .animation(e('move-in 500ms')); 299 | } 300 | 301 | .content { 302 | .animation(e('move-in 600ms')); 303 | } 304 | } 305 | } 306 | 307 | .main-view.ng-leave .stream { 308 | 309 | .small-icon, .stream-line { 310 | .animation(e('scale-out 350ms')); 311 | } 312 | 313 | .stream-group:nth-child(-n+5) { 314 | .day-title, .content { 315 | .animation(e('move-out 500ms')); 316 | } 317 | } 318 | } 319 | 320 | @media (max-width: 700px) { 321 | .stream { 322 | .stream-line { 323 | margin-left: 18px; 324 | } 325 | 326 | h2 { 327 | margin-left: 15px; 328 | } 329 | 330 | h3 { 331 | float: none; 332 | width: 100%; 333 | margin-left: 60px; 334 | } 335 | 336 | .small-icon { 337 | left: 0; 338 | } 339 | 340 | .content { 341 | max-width: 100%; 342 | padding-left: 60px; 343 | padding-top: 10px !important; 344 | } 345 | 346 | .lastfm h3 { 347 | display: none; 348 | } 349 | 350 | .instagram .icon.play-video { 351 | left: 110px; 352 | top: 60px; 353 | } 354 | 355 | .youtube .icon.play-video { 356 | top: 55px; 357 | left: 160px; 358 | } 359 | } 360 | } 361 | 362 | @media (max-width:480px) { 363 | .stream { 364 | overflow-x: hidden; 365 | 366 | .content { 367 | padding-right: 10px; 368 | } 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /client/app/styles/less/pages/tumblr.less: -------------------------------------------------------------------------------- 1 | .tumblr-page { 2 | position: relative; 3 | opacity: 0; 4 | .clearfix; 5 | padding-top: 10px; 6 | 7 | iframe { 8 | display: block; 9 | } 10 | 11 | .post { 12 | padding: 40px 100px 20px 0; 13 | border-top: 1px solid @line-color; 14 | } 15 | 16 | .post:first-child { 17 | border-top: 0; 18 | } 19 | 20 | .date { 21 | color: @alt-text-color; 22 | margin-bottom: 20px; 23 | position: relative; 24 | } 25 | 26 | .title, .body, .player { 27 | position: relative; 28 | } 29 | 30 | .pictures { 31 | display: inline-block; 32 | margin-right: 5px; 33 | margin-bottom: 10px; 34 | position: relative; 35 | 36 | &.multiple .pic, &.multiple .link-pic { 37 | width: 150px; 38 | height: 150px; 39 | 40 | &:hover { 41 | .transform(@transform:scale(1.05)); 42 | } 43 | } 44 | } 45 | 46 | .pic { 47 | width: 300px; 48 | height: 300px; 49 | display: block; 50 | background-size: cover; 51 | background-position: 50% 50%; 52 | cursor: pointer; 53 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 54 | .transition(e('all 320ms ease-out')); 55 | 56 | &:hover { 57 | .transform(@transform:scale(1.03)); 58 | } 59 | } 60 | 61 | .link-pic { 62 | width: 300px; 63 | height: 300px; 64 | display: block; 65 | background-size: cover; 66 | background-position: 50% 50%; 67 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 68 | } 69 | 70 | .player { 71 | margin-bottom: 15px; 72 | 73 | iframe { 74 | display: block; 75 | } 76 | } 77 | } 78 | 79 | .tumblr-page.visible { 80 | opacity: 1; 81 | } 82 | 83 | .tumblr-page.animate-enter { 84 | .animation(e('fade-in 200ms ease-in backwards')); 85 | 86 | .post:nth-child(-n+3) { 87 | .title { 88 | .animation(e('move-in 500ms')); 89 | } 90 | 91 | .player { 92 | .animation(e('move-in 700ms')); 93 | } 94 | 95 | .date { 96 | .animation(e('move-in 600ms')); 97 | } 98 | 99 | .body { 100 | .animation(e('move-in 500ms')); 101 | } 102 | } 103 | 104 | } 105 | 106 | .main-view.ng-leave .tumblr-page { 107 | .post:nth-child(-n+3) { 108 | .title { 109 | .animation(e('move-out 500ms')); 110 | } 111 | 112 | .player { 113 | .animation(e('move-out 700ms')); 114 | } 115 | 116 | .date { 117 | .animation(e('move-out 600ms')); 118 | } 119 | 120 | .body { 121 | .animation(e('move-out 350ms')); 122 | } 123 | } 124 | } 125 | 126 | @media (max-width: 480px) { 127 | .tumblr-page .post { 128 | padding-right: 0; 129 | 130 | img { 131 | max-width: 100%; 132 | } 133 | } 134 | } 135 | 136 | -------------------------------------------------------------------------------- /client/app/styles/less/pages/twitter.less: -------------------------------------------------------------------------------- 1 | .twitter-page { 2 | position: relative; 3 | opacity: 0; 4 | .clearfix; 5 | 6 | .user { 7 | position: relative; 8 | float: left; 9 | width: 34%; 10 | padding: 40px 0; 11 | 12 | .banner { 13 | width: 100%; 14 | height: 150px; 15 | background-size: cover; 16 | background-position: 50% 50%; 17 | margin-bottom: 40px; 18 | .border-radius(4px); 19 | } 20 | 21 | .over-shadow { 22 | width: 100%; 23 | height: 150px; 24 | .border-radius(4px); 25 | position: absolute; 26 | top: 40px; 27 | z-index: 1; 28 | background: -moz-linear-gradient(top, rgba(0,0,0,0) 30%, rgba(0,0,0,0.6) 100%); 29 | background: -webkit-linear-gradient(top, rgba(0,0,0,0) 30%, rgba(0,0,0,0.6) 100%); 30 | background: linear-gradient(to bottom, rgba(0,0,0,0) 30%, rgba(0,0,0,0.6) 100%); 31 | } 32 | 33 | .picture { 34 | width: 80px; 35 | height: 80px; 36 | .border-radius(40px); 37 | overflow: hidden; 38 | display: inline-block; 39 | .box-shadow(@shadow: 0 2px 6px rgba(0,0,0,.9)); 40 | position: absolute; 41 | top: 140px; 42 | left: 20px; 43 | z-index: 2; 44 | 45 | img { 46 | width: 80px; 47 | height: 80px; 48 | } 49 | } 50 | 51 | h2 { 52 | position: absolute; 53 | top: 157px; 54 | left: 120px; 55 | z-index: 2; 56 | } 57 | 58 | a.username { 59 | position: absolute; 60 | top: 193px; 61 | left: 120px; 62 | z-index: 2; 63 | } 64 | 65 | h3 { 66 | position: relative; 67 | } 68 | 69 | p { 70 | position: relative; 71 | padding: 10px 0; 72 | } 73 | 74 | .stats { 75 | .clearfix; 76 | .border-radius(4px); 77 | overflow: hidden; 78 | background-color: @dark-bg-color; 79 | margin-bottom: 30px; 80 | 81 | li { 82 | width: 33%; 83 | float: left; 84 | text-align: center; 85 | padding: 10px 0; 86 | font-size: 12px; 87 | line-height: 20px; 88 | } 89 | 90 | strong { 91 | font-weight: 300; 92 | color: @link-color; 93 | display: block; 94 | font-size: 16px; 95 | line-height: 26px; 96 | } 97 | } 98 | 99 | .photos { 100 | margin: 5px 0 0 -10px; 101 | position: relative; 102 | .clearfix; 103 | 104 | .photo { 105 | width: 67px; 106 | height: 67px; 107 | margin: 10px 0 0 10px; 108 | display: block; 109 | float: left; 110 | background-size: cover; 111 | background-position: 50% 50%; 112 | cursor: pointer; 113 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 114 | .transition(e('all 320ms ease-out')); 115 | 116 | &:hover { 117 | .transform(@transform:scale(1.07)); 118 | } 119 | } 120 | } 121 | } 122 | 123 | .tweets { 124 | float: left; 125 | width: 66%; 126 | padding: 40px 0 40px 40px; 127 | 128 | ul, h2 { 129 | display: block; 130 | } 131 | 132 | h2 { 133 | margin-bottom: 10px; 134 | } 135 | 136 | li { 137 | border-top: 1px solid @line-color; 138 | padding: 20px 0 10px 0; 139 | position: relative; 140 | } 141 | 142 | .pictures { 143 | display: inline-block; 144 | margin-right: 5px; 145 | margin-bottom: 15px; 146 | position: relative; 147 | } 148 | 149 | .pic { 150 | width: 73px; 151 | height: 73px; 152 | display: block; 153 | background-size: cover; 154 | background-position: 50% 50%; 155 | cursor: pointer; 156 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 157 | .transition(e('all 320ms ease-out')); 158 | 159 | &:hover { 160 | .transform(@transform:scale(1.07)); 161 | } 162 | } 163 | 164 | .avatar { 165 | width: 40px; 166 | height: 40px; 167 | .border-radius(20px); 168 | overflow: hidden; 169 | display: block; 170 | position: absolute; 171 | top: 25px; 172 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 173 | .transition(e('all 320ms ease-out')); 174 | 175 | &:hover { 176 | .transform(@transform:scale(1.07)); 177 | } 178 | 179 | img { 180 | width: 40px; 181 | height: 40px; 182 | } 183 | } 184 | 185 | .content { 186 | margin-left: 55px; 187 | position: relative; 188 | 189 | h4 a { 190 | color: @text-color; 191 | } 192 | } 193 | 194 | .username { 195 | font-weight: 300; 196 | margin-left: 5px; 197 | } 198 | 199 | .date { 200 | color: @alt-text-color; 201 | font-size: 12px; 202 | margin-left: 10px; 203 | } 204 | 205 | .icon.play-video { 206 | position: absolute; 207 | z-index: 1; 208 | left: 10px; 209 | top: 10px; 210 | pointer-events: none; 211 | font-size: 30px; 212 | } 213 | } 214 | } 215 | 216 | .twitter-page.visible { 217 | opacity: 1; 218 | } 219 | 220 | .twitter-page.animate-enter { 221 | .animation(e('fade-in 200ms ease-in backwards')); 222 | 223 | .user { 224 | .picture { 225 | .animation(e('scale-in 350ms')); 226 | } 227 | 228 | h2, .username { 229 | .animation(e('scale-in 500ms')); 230 | } 231 | 232 | p, h3 { 233 | .animation(e('move-in 500ms')); 234 | } 235 | 236 | .stats li { 237 | .animation(e('scale-in 500ms')); 238 | } 239 | 240 | .photo { 241 | .animation(e('half-scale-in 500ms')); 242 | } 243 | } 244 | 245 | .tweets .tweet:nth-child(-n+10) { 246 | .avatar { 247 | .animation(e('scale-in 350ms')); 248 | } 249 | 250 | .content { 251 | .animation(e('move-in 400ms')); 252 | } 253 | } 254 | } 255 | 256 | .main-view.ng-leave .twitter-page { 257 | .user { 258 | .picture { 259 | .animation(e('scale-in 400ms reverse')); 260 | } 261 | 262 | .stats li, .photo { 263 | .animation(e('scale-in 700ms reverse')); 264 | } 265 | 266 | p, h3 { 267 | .animation(e('move-out 700ms')); 268 | } 269 | } 270 | 271 | .tweets .tweet:nth-child(-n+10) { 272 | .avatar { 273 | .animation(e('scale-in 350ms reverse')); 274 | } 275 | 276 | .content { 277 | .animation(e('move-out 700ms')); 278 | } 279 | } 280 | } 281 | 282 | @media (max-width: 1035px) { 283 | .twitter-page .user .photos .photo { 284 | width: 50px; 285 | height: 50px; 286 | } 287 | } 288 | 289 | @media (max-width: 900px) { 290 | .twitter-page { 291 | .user { 292 | width: 40%; 293 | } 294 | 295 | .tweets { 296 | width: 60%; 297 | } 298 | } 299 | } 300 | 301 | @media (max-width: 700px) { 302 | .twitter-page { 303 | .user { 304 | float: none; 305 | width: 100%; 306 | padding-bottom: 0; 307 | 308 | 309 | h3, .photos { 310 | display: none; 311 | } 312 | } 313 | 314 | .tweets { 315 | float: none; 316 | width: 100%; 317 | padding: 0; 318 | } 319 | } 320 | } -------------------------------------------------------------------------------- /client/app/styles/less/pages/youtube.less: -------------------------------------------------------------------------------- 1 | .youtube-page { 2 | position: relative; 3 | opacity: 0; 4 | .clearfix; 5 | 6 | .user { 7 | position: relative; 8 | padding: 40px 0 15px 0; 9 | height: 190px; 10 | .clearfix; 11 | 12 | .banner { 13 | width: 61%; 14 | height: 105px; 15 | background-size: cover; 16 | background-position: 50% 50%; 17 | margin-bottom: 40px; 18 | .border-radius(4px); 19 | } 20 | 21 | .picture { 22 | width: 70px; 23 | height: 70px; 24 | .border-radius(40px); 25 | overflow: hidden; 26 | display: inline-block; 27 | .box-shadow(@shadow: 0 2px 6px rgba(0,0,0,.9)); 28 | position: absolute; 29 | top: 115px; 30 | left: 20px; 31 | z-index: 2; 32 | 33 | img { 34 | width: 70px; 35 | height: 70px; 36 | } 37 | } 38 | 39 | .name { 40 | position: absolute; 41 | top: 150px; 42 | left: 110px; 43 | z-index: 2; 44 | } 45 | 46 | .stat { 47 | position: absolute; 48 | left: 270px; 49 | top: 153px; 50 | 51 | strong { 52 | color: @link-color; 53 | } 54 | } 55 | } 56 | 57 | .posts { 58 | .clearfix; 59 | margin-top: 30px; 60 | margin-left: -30px; 61 | 62 | .post { 63 | width: 28%; 64 | float: left; 65 | margin: 0 0 30px 30px; 66 | position: relative; 67 | .transition(e('all 320ms ease-out')); 68 | 69 | &:hover { 70 | .transform(@transform:scale(1.02)); 71 | 72 | .info { 73 | opacity: 1.0; 74 | } 75 | } 76 | 77 | img { 78 | width: 100%; 79 | cursor: pointer; 80 | display: block; 81 | .box-shadow(@shadow: 0 1px 4px rgba(0,0,0,.6)); 82 | } 83 | 84 | .icon.play-video { 85 | position: absolute; 86 | z-index: 1; 87 | left: 50%; 88 | top: 50%; 89 | margin-left: -25px; 90 | margin-top: -25px; 91 | pointer-events:none; 92 | } 93 | } 94 | 95 | .info { 96 | width: 100%; 97 | height: 100%; 98 | position: absolute; 99 | top: 0; 100 | background-color: rgba(0,0,0,0.7); 101 | pointer-events:none; 102 | z-index: 2; 103 | opacity: 0; 104 | .transition(e('all 320ms ease-out')); 105 | 106 | .title { 107 | font-weight: 400; 108 | color: @text-color; 109 | position: relative; 110 | display: block; 111 | margin: 10px; 112 | } 113 | } 114 | } 115 | } 116 | 117 | .youtube-page.visible { 118 | opacity: 1; 119 | } 120 | 121 | .youtube-page.animate-enter { 122 | .animation(e('fade-in 200ms ease-in backwards')); 123 | 124 | .user { 125 | .picture { 126 | .animation(e('scale-in 350ms')); 127 | } 128 | 129 | h2 { 130 | .animation(e('scale-in 500ms')); 131 | } 132 | 133 | p { 134 | .animation(e('scale-in 600ms')); 135 | } 136 | } 137 | 138 | .posts .pic { 139 | .animation(e('half-scale-in 500ms')); 140 | } 141 | } 142 | 143 | .main-view.ng-leave .youtube-page { 144 | .user { 145 | .picture { 146 | .animation(e('scale-in 400ms reverse')); 147 | } 148 | } 149 | 150 | .posts .pic { 151 | .animation(e('half-scale-in 500ms reverse')); 152 | } 153 | } 154 | 155 | @media (max-width: 900px) { 156 | .youtube-page { 157 | .user .banner { 158 | width: 80%; 159 | } 160 | 161 | .posts .post { 162 | width: 35%; 163 | } 164 | } 165 | } 166 | 167 | @media (max-width: 700px) { 168 | .youtube-page { 169 | .user .banner { 170 | width: 100%; 171 | } 172 | 173 | .posts { 174 | margin-left: -5%; 175 | 176 | .post { 177 | width: 45%; 178 | margin: 0 0 20px 5%; 179 | } 180 | } 181 | } 182 | } 183 | 184 | @media (max-width: 550px) { 185 | .youtube-page { 186 | .user { 187 | height: auto; 188 | 189 | .stat { 190 | position: relative; 191 | left: 0; 192 | top: 10px; 193 | clear: both; 194 | } 195 | } 196 | 197 | .posts { 198 | margin-left: 0; 199 | margin-top: 0; 200 | 201 | .post { 202 | width: 100%; 203 | margin: 0 0 15px 0; 204 | float: none; 205 | } 206 | } 207 | } 208 | } 209 | 210 | -------------------------------------------------------------------------------- /client/app/styles/less/variables.less: -------------------------------------------------------------------------------- 1 | @bg-color: #202a2f; 2 | @dark-bg-color: #182125; 3 | @text-color: #eee; 4 | @alt-text-color: #5d7679; 5 | @text-color-strong: #fff; 6 | @link-color: #79d4d5; 7 | @line-color: #263435; 8 | 9 | @text-color-on-white: #282828; 10 | @alt-text-color-on-white: #909090; 11 | @link-color-on-white: #00B4B9; 12 | @light-bg-color: #ddd; -------------------------------------------------------------------------------- /client/app/templates/dribbble/details.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/app/templates/dribbble/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |

{{ user.name }}

8 | @{{ user.username }} 9 |
10 |
    11 |
  • Shots {{ user.shots }}
  • 12 |
  • Following {{ user.following }}
  • 13 |
  • Followers {{ user.followers }}
  • 14 |
15 |
16 |

{{ user.bio }}

17 |
18 |
    19 |
  • 20 | 21 | 28 |
  • 29 |
  • 30 |
31 |
32 |
Loading...
-------------------------------------------------------------------------------- /client/app/templates/dribbble/post.html: -------------------------------------------------------------------------------- 1 |

{{ item.date | date: 'h:mm a'}}

2 | 3 |
4 | 5 |

{{ item.title }}

6 |
-------------------------------------------------------------------------------- /client/app/templates/foursquare/checkin.html: -------------------------------------------------------------------------------- 1 |

{{ item.date | date: 'h:mm a'}}

2 | 3 |
4 |
5 | 6 |
7 |
8 |
{{ item.title }}
9 |
10 | {{ item.event }} - 11 | {{ item.category }} - 12 | {{ item.city }}, {{item.state}} 13 |
14 |
15 |
-------------------------------------------------------------------------------- /client/app/templates/foursquare/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |

{{ user.name }}

7 | {{ user.location }} 8 |

9 |
    10 |
  • Check-ins {{ user.checkins }}
  • 11 |
  • Friends {{ user.friends }}
  • 12 |
13 |

Recent Check-ins

14 |
    15 |
  • 16 |
    17 | 18 |
    19 |
    20 | 21 |
    22 | {{ item.event }} - 23 | {{ item.category }} - 24 | {{ item.city }}, {{item.state}} 25 |
    26 |
    27 |
    28 |
  • 29 |
30 |
31 |
32 |
33 |

{{ monthTitleDate }}

34 | {{ totalCheckins }} Check-ins 35 | 36 | 37 |
38 |
39 |
40 |
41 |
42 |
43 |
{{ item.title }}
44 |
45 |
46 |
47 |
    48 |
  • 49 |
    50 | 51 |
    52 |
    {{category.title}}
    53 |
    {{category.count}} timetimes
    54 |
  • 55 |
56 |
57 |
58 |
Loading...
-------------------------------------------------------------------------------- /client/app/templates/github/activity.html: -------------------------------------------------------------------------------- 1 |

{{ item.date | date: 'h:mm a'}}

2 | 3 |
4 |

Pushed to 5 | {{ item.repo }}a private repo on Github. 6 |

7 |
-------------------------------------------------------------------------------- /client/app/templates/github/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |

{{ user.name }}

7 | {{ user.username }} 8 |
    9 |
  • Repos {{ user.repos }}
  • 10 |
  • Following {{ user.following }}
  • 11 |
  • Followers {{ user.followers }}
  • 12 |
13 |

Repositories

14 |
    15 |
  • 16 | {{ repo.name }} 17 |

    {{ repo.description }}

    18 |
      19 |
    • {{ repo.language }}
    • 20 |
    • {{ repo.favorites }}
    • 21 |
    • {{ repo.forks }}
    • 22 |
    23 |
  • 24 |
25 |
26 |
27 |

Recent Activity

28 |
    29 |
  • 30 | 31 |

    32 |
    33 | 34 |
  • 35 |
36 |
37 |
38 |
Loading...
-------------------------------------------------------------------------------- /client/app/templates/instagram/details.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/app/templates/instagram/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |

{{ user.full_name }}

8 | @{{ user.username }} 9 |
10 |
    11 |
  • Posts {{ user.counts.media }}
  • 12 |
  • Following {{ user.counts.follows }}
  • 13 |
  • Followers {{ user.counts.followed_by }}
  • 14 |
15 |
16 |

{{ user.bio }}

17 |
18 |
    19 |
  • 20 | 21 | 22 |
    23 | {{ item.likes }} 24 | {{ item.comments }} 25 |
    26 |
  • 27 |
  • 28 |
29 | See more on Instagram... 30 |
31 |
Loading...
-------------------------------------------------------------------------------- /client/app/templates/instagram/post.html: -------------------------------------------------------------------------------- 1 |

{{ item.date | date: 'h:mm a'}}

2 | 3 |
4 | 5 | 6 |

7 |
-------------------------------------------------------------------------------- /client/app/templates/lastfm/activity.html: -------------------------------------------------------------------------------- 1 |

 

2 | 3 |
4 |

Listened to {{ item.tracks[0].title }} by {{ item.tracks[0].artist }} and {{ item.plays - 1 }} other tracks{{ item.plays - 1 }} other track on Spotify.

5 |

Listened to {{ item.plays }} tracks{{ item.plays }} track on Spotify.

6 | 7 |
8 | -------------------------------------------------------------------------------- /client/app/templates/lastfm/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |

{{ user.name }}

7 | {{ user.username }} 8 |

Recent Tracks

9 |
    10 |
  • 11 |
    12 | 13 | 14 |
    15 |
    16 |
    {{ item.title }} - {{ item.artist }}
    17 |
    18 |
    19 |
  • 20 |
21 |
22 |
23 |

Top Artists

24 | 33 |

Top Albums

34 |
    35 |
  • 36 |
    37 |
    38 | {{ album.name }} 39 |
    {{ album.artist }}
    40 |
    {{ album.count }} plays
    41 |
    42 |
  • 43 |
44 |

Top Tracks

45 |
    46 |
  • 47 |
    48 |
    49 | {{ track.name }} 50 |
    {{ track.artist }}
    51 |
    {{ track.count }} plays
    52 |
    53 |
  • 54 |
55 |
56 |
57 |
Loading...
-------------------------------------------------------------------------------- /client/app/templates/nav.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 |

Rodrigo Neri

8 |
9 |
10 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | -------------------------------------------------------------------------------- /client/app/templates/stream/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

{{ group.title }}

5 |
    6 |
  • 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
  • 16 |
17 |
18 |
19 |
Loading...
20 | -------------------------------------------------------------------------------- /client/app/templates/tumblr/details.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/app/templates/tumblr/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

{{ post.title }}

5 |
6 |
7 |
8 |
9 |
10 |
11 | 12 |
13 |

14 |
15 |
16 |

{{ post.title }}

17 |
18 |
19 |

20 |
21 |
22 |

{{ post.title }}

23 |
24 |
25 | 26 |
27 |
28 |
29 |
30 |
{{ post.quote }}
31 |

- {{ post.source }}

32 |
33 |
34 |

Post Not Found!

35 |
36 |
Loading...
37 | -------------------------------------------------------------------------------- /client/app/templates/tumblr/post.html: -------------------------------------------------------------------------------- 1 |

{{ item.date | date: 'h:mm a'}}

2 | 3 |
4 |

New blog post!

5 | {{ item.title }} 6 |
7 |
8 |
9 | 10 |
11 |

12 |
13 |
14 |

{{ item.title }}

15 |
16 |

17 |
18 |
19 |

{{ item.title }}

20 |
21 | 22 |
23 |
24 |
25 |
{{ item.quote }}
26 |

- {{ item.source }}

27 |
28 | -------------------------------------------------------------------------------- /client/app/templates/twitter/details.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/app/templates/twitter/index.html: -------------------------------------------------------------------------------- 1 | 40 |
Loading...
-------------------------------------------------------------------------------- /client/app/templates/twitter/post.html: -------------------------------------------------------------------------------- 1 |

{{ item.date | date: 'h:mm a'}}

2 | 3 |
4 |

5 |
6 | 7 | 8 |
9 |
-------------------------------------------------------------------------------- /client/app/templates/youtube/details.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/app/templates/youtube/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 |

{{ user.name }}

8 |

{{ user.subscribers }} Subscribers

9 |
10 |
11 |
    12 |
  • 13 | 14 | 15 |
    16 |

    {{ item.title }}

    17 |
    18 |
  • 19 |
  • 20 |
21 |
22 |
Loading...
-------------------------------------------------------------------------------- /client/app/templates/youtube/post.html: -------------------------------------------------------------------------------- 1 |

{{ item.date | date: 'h:mm a'}}

2 | 3 |
4 | 5 | 6 |

{{ item.title }}

7 |
-------------------------------------------------------------------------------- /client/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular": "1.4.0", 6 | "angular-animate": "1.4.0", 7 | "angular-route": "1.4.0", 8 | "angular-sanitize": "1.4.0", 9 | "moment": "2.13.0", 10 | "angular-moment": "0.10.3", 11 | "angular-modal-service": "0.10.1", 12 | "angular-lazy-img": "1.2.2", 13 | "google-maps": "3.2.1" 14 | }, 15 | "devDependencies": { 16 | "angular-mocks": "1.4.0" 17 | }, 18 | "appPath": "app", 19 | "moduleName": "clientApp", 20 | "resolutions": { 21 | "angular": "1.4.0", 22 | "moment": "2.13.0" 23 | } 24 | } -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "devDependencies": { 5 | "autoprefixer-core": "^5.2.1", 6 | "grunt": "^0.4.5", 7 | "grunt-angular-templates": "^0.5.7", 8 | "grunt-concurrent": "^1.0.0", 9 | "grunt-contrib-clean": "^0.6.0", 10 | "grunt-contrib-concat": "^0.5.0", 11 | "grunt-contrib-connect": "^0.9.0", 12 | "grunt-contrib-copy": "^0.7.0", 13 | "grunt-contrib-cssmin": "^0.12.0", 14 | "grunt-contrib-htmlmin": "^0.4.0", 15 | "grunt-contrib-imagemin": "^1.0.0", 16 | "grunt-contrib-jshint": "^0.11.0", 17 | "grunt-contrib-less": "^1.3.0", 18 | "grunt-contrib-uglify": "^0.7.0", 19 | "grunt-contrib-watch": "^0.6.1", 20 | "grunt-filerev": "^2.1.2", 21 | "grunt-google-cdn": "^0.4.3", 22 | "grunt-jscs": "^1.8.0", 23 | "grunt-newer": "^1.1.0", 24 | "grunt-ng-annotate": "^0.9.2", 25 | "grunt-postcss": "^0.5.5", 26 | "grunt-svgmin": "^2.0.0", 27 | "grunt-usemin": "^3.0.0", 28 | "grunt-wiredep": "^2.0.0", 29 | "jit-grunt": "^0.9.1", 30 | "jshint-stylish": "^1.0.0", 31 | "moment": "^2.13.0", 32 | "time-grunt": "^1.0.0", 33 | "twitter": "^1.2.5" 34 | }, 35 | "engines": { 36 | "node": ">=0.10.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Setup Instructions 2 | 3 | There are a few steps in order to get Syte2 configured, but don't worry they are pretty easy. 4 | 5 | 1. [Getting started and initial content changes](start.md) 6 | 2. [Running the project locally](running.md) 7 | 3. [Setting up Twitter](twitter.md) 8 | 4. [Setting up Github](github.md) 9 | 5. [Setting up Dribbble](dribbble.md) 10 | 6. [Setting up Spotify & Last.fm](lastfm.md) 11 | 7. [Setting up Instagram](instagram.md) 12 | 8. [Setting up YouTube](youtube.md) 13 | 9. [Setting up Foursquare](foursquare.md) 14 | 10. [Setting up Tumblr](tumblr.md) 15 | 16 | ## Deployment Instructions 17 | 18 | For now I've only deployed my website to Heroku since it's free to start out. I would love to see it deployed to different services. 19 | 20 | 1. [Heroku deployment instructions](heroku.md) 21 | -------------------------------------------------------------------------------- /docs/TODO.md: -------------------------------------------------------------------------------- 1 | List of services to add integration for: 2 | 3 | - Twitter [DONE] 4 | - Github [DONE] 5 | - Dribbble [DONE] 6 | - Instagram [DONE] 7 | - Tumblr [DONE] 8 | - Foursquare [DONE] 9 | - Last.fm/Spotify [DONE] 10 | - YouTube [DONE] 11 | - Wordpress 12 | - Bitbucket 13 | - Stack Overflow 14 | - Fitbit 15 | 16 | -------------------------------------------------------------------------------- /docs/docker.md: -------------------------------------------------------------------------------- 1 | ##Deploying with Docker 2 | 3 | This method will allow you to run project on any machine that can run Docker. For example, I use AWS EC2 to host it. 4 | 5 | 1. Install [Docker](https://www.docker.com/) 6 | 2. Follow all instructions at [Getting started and initial content changes](start.md) 7 | 3. Build Docker container by `docker build -t syte2 .` 8 | 4. Run Docker contailer by `docker run --restart=always --network=host syte2` 9 | 10 | Now you should be able to run project 11 | -------------------------------------------------------------------------------- /docs/dribbble.md: -------------------------------------------------------------------------------- 1 | ## Setting up Dribbble 2 | 3 | To get started go to and click on **Register a New Application**. 4 | 5 | Enter **Name**, **Description**, **Website URL** and **Callback URL**. For the callback URL enter `http://localhost:3000/dribbble/auth`. Once you are done registering you will be given a **Client ID** and **Client Secret**. 6 | 7 | Once you have your access token open the `server > .env` file and enter: 8 | 9 | * **Client ID** string under `DRIBBBLE_CLIENT_ID` 10 | * **Client Secret** string under `DRIBBBLE_CLIENT_SECRET` 11 | 12 | Also make sure to set: 13 | 14 | * `DRIBBBLE_INTEGRATION_DISABLED` to false 15 | * `DRIBBBLE_OAUTH_ENABLED` to true 16 | * `DRIBBBLE_UPDATE_FREQ_MINUTES` to the number of minutes you want to wait before updating the data. `Note` A small number is not always the best and it might make Syte2 slow, use a number close to how often you post a shot to dribbble. 17 | 18 | Next, restart the server and open the following url . you will be taken to Dribbble's website and will be asked to sign in and authorize your application. After you authorized you will be taken back to Syte2 and you will be given your **Access Token**. Open the `server > .env` file again and enter: 19 | 20 | * **Access Token** under `DRIBBBLE_ACCESS_TOKEN` 21 | 22 | Next, restart the server and open the following url . If everything is setup right, this will fetch some of your recent posts and save them to the database. If you have posted something recently, you should be able to go to and see that post in your stream and at this point your Dribbble page should be working. 23 | 24 | --- 25 | 26 | Next: [Setting up Spotify & Last.fm](lastfm.md) 27 | -------------------------------------------------------------------------------- /docs/foursquare.md: -------------------------------------------------------------------------------- 1 | ### Setting up Foursquare 2 | 3 | To get started, create a new application on Foursquare for your website by going to . 4 | 5 | Enter the **Application Name**, **Welcome page url** and **Redirect URIs**. For the redirect URL enter `http://localhost:3000/foursquare/auth`. Once you are done registering you will be given a **Client ID** and **Client Secret**. 6 | 7 | Once you have those four items open the `server > .env` file and enter: 8 | 9 | * **Client ID** string under `FOURSQUARE_CLIENT_ID` 10 | * **Client Secret** string under `FOURSQUARE_CLIENT_SECRET` 11 | 12 | Also make sure to set: 13 | 14 | * `FOURSQUARE_INTEGRATION_DISABLED` to false 15 | * `FOURSQUARE_OAUTH_ENABLED` to true 16 | * `FOURSQUARE_UPDATE_FREQ_MINUTES` to the number of minutes you want to wait before updating the data. `Note` A small number is not always the best and it might make Syte2 slow, use a number close to how often you use Foursquare. 17 | 18 | Next, restart the server and open the following url , you will be taken to Foursquare's website and will be asked to sign in and authorize your application. After you authorized you will be taken back to Syte2 and you will be given your **Access Token**. Open the `server > .env` file again and enter: 19 | 20 | * **Access Token** under `FOURSQUARE_ACCESS_TOKEN` 21 | 22 | Next, restart the server and open the following url . If everything is setup right, this will fetch some of your recent check-ins and save them to the database. Next we need to setup **map** we use in the Foursquare page. 23 | 24 | ## Setting Google Maps 25 | 26 | In order to set up Google Maps we need to get an API Key from Google. If you haven't done so, login to the Google Developers Console by going to . Navigate to `API Manager > Credentials` and create a new project, after that's done go to the [OAuth consent screen](https://console.developers.google.com/apis/credentials/consent) and enter a **Product name** (all other fields are optional). 27 | 28 | Next click on `Create credentials > API Key > Browser Key` and enter the following: 29 | 30 | * **Name:** Syte 31 | * **Accept requests from these HTTP referrers:** Enter ***http://localhost:3000*** and your webstie url (ex. ***http://rigoneri.com***) 32 | 33 | Once you are done you will be given a **API Key**, open the file `client > app > scripts > env.js` and enter the key under `GOOGLE_MAPS_KEY`. 34 | 35 | Restart the server and re-run `grunt serve` from the client folder. You should now be able to go to and see that any recent check-ins in your stream and at this point your Foursquare page should also be working. 36 | 37 | --- 38 | 39 | Next: [Setting up Tumblr](tumblr.md) 40 | -------------------------------------------------------------------------------- /docs/github.md: -------------------------------------------------------------------------------- 1 | ## Setting up Github 2 | 3 | To get started sign in to GitHub and go to to register your application. 4 | 5 | Enter the **Application Name**, **Homepage URL** and **Authorization callback URL**. For the callback URL enter `http://localhost:3000/github/auth`. Once you are done registering you will be given a **Client ID** and **Client Secret**. 6 | 7 | Once you have those four items open the `server > .env` file and enter: 8 | 9 | * **Client ID** string under `GITHUB_CLIENT_ID` 10 | * **Client Secret** string under `GITHUB_CLIENT_SECRET` 11 | 12 | Also make sure to set: 13 | 14 | * `GITHUB_INTEGRATION_DISABLED` to false 15 | * `GITHUB_OAUTH_ENABLED` to true 16 | * `GITHUB_USERNAME` to your twitter username, without the *@* 17 | * `GITHUB_UPDATE_FREQ_MINUTES` to the number of minutes you want to wait before updating the data. `Note` A small number is not always the best and it might make Syte2 slow, use a number close to how often you use github. 18 | 19 | Next, restart the server and open the following url , you will be taken to GitHub's website and will be asked to sign in and authorize your application. After you authorized you will be taken back to Syte2 and you will be given your **Access Token**. Open the `server > .env` file again and enter: 20 | 21 | * **Access Token Secret** under `GITHUB_ACCESS_TOKEN` 22 | 23 | Next, restart the server and open the following url . If everything is setup right, this will fetch some of your recent activity and save them to the database. If you have committed something recently, you should be able to go to and see that activity in your stream and at this point your Github page should be working. 24 | 25 | --- 26 | 27 | Next: [Setting up Dribbble](dribbble.md) 28 | -------------------------------------------------------------------------------- /docs/heroku.md: -------------------------------------------------------------------------------- 1 | ## Deploying to Heroku 2 | 3 | First, sign up to [Heroku](http://heroku.com) and download and install [Heroku Toolbet](https://devcenter.heroku.com/articles/getting-started-with-nodejs#set-up), using the terminal in the Syte2 project make sure you can login to heroku `heroku login`. 4 | 5 | Next, navigate to the [Heroku dashboard](https://dashboard.heroku.com/apps) and create a new app. Enter a **App Name**, this needs to be unique, for this example we will use **syte2**. In the ***Resources*** page under **Add-ons**, search for `mLab MongoDB` and select `Sandbox - Free` as the plan name. 6 | 7 | Next, navigate to the ***Settings*** page and under **Config Variables** click on ***Reveal Config Vars***, you should already see `MONGODB_URI` already set, now we need to add some variables from the `server > .env` file, enter the following: 8 | 9 | * `TZ` to your timezone, pick one from [this list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) if you don't know which one to use. (ex. `America/Chicago`) 10 | * `SETUP_ENABLED` to `true` 11 | 12 | --- 13 | 14 | If you integrated with Twitter enter: 15 | 16 | * `TWITTER_USERNAME` 17 | * `TWITTER_CONSUMER_KEY` 18 | * `TWITTER_CONSUMER_SECRET` 19 | * `TWITTER_ACCESS_TOKEN_KEY` 20 | * `TWITTER_ACCESS_TOKEN_SECRET` 21 | * `TWITTER_UPDATE_FREQ_MINUTES` 22 | 23 | If you didn't integrate with Twitter enter: 24 | 25 | * `TWITTER_INTEGRATION_DISABLED` to `true` 26 | 27 | --- 28 | 29 | If you integrated with Instagram enter: 30 | 31 | * `INSTAGRAM_ACCESS_TOKEN` 32 | * `INSTAGRAM_UPDATE_FREQ_MINUTES` 33 | 34 | If you didn't integrate with Instagram enter: 35 | 36 | * `INSTAGRAM_INTEGRATION_DISABLED` to `true` 37 | 38 | --- 39 | 40 | If you integrated with Foursquare enter: 41 | 42 | * `FOURSQUARE_ACCESS_TOKEN` 43 | * `FOURSQUARE_UPDATE_FREQ_MINUTES` 44 | 45 | If you didn't integrate with Foursquare enter: 46 | 47 | * `FOURSQUARE_INTEGRATION_DISABLED` to `true` 48 | 49 | --- 50 | 51 | If you integrated with Dribbble enter: 52 | 53 | * `DRIBBBLE_ACCESS_TOKEN` 54 | * `DRIBBBLE_UPDATE_FREQ_MINUTES` 55 | 56 | If you didn't integrate with Dribbble enter: 57 | 58 | * `DRIBBBLE_INTEGRATION_DISABLED` to `true` 59 | 60 | --- 61 | 62 | If you integrated with Tumblr enter: 63 | 64 | * `TUMBLR_BLOG` 65 | * `TUMBLR_API_KEY` 66 | * `TUMBLR_UPDATE_FREQ_MINUTES` 67 | 68 | If you didn't integrate with Tumblr enter: 69 | 70 | * `TUMBLR_INTEGRATION_DISABLED` to `true` 71 | 72 | --- 73 | 74 | If you integrated with Github enter: 75 | 76 | * `GITHUB_ACCESS_TOKEN` 77 | * `GITHUB_USERNAME` 78 | * `GITHUB_UPDATE_FREQ_MINUTES` 79 | 80 | If you didn't integrate with Github enter: 81 | 82 | * `GITHUB_INTEGRATION_DISABLED` to `true` 83 | 84 | --- 85 | 86 | If you integrated with Last.fm enter: 87 | 88 | * `LASTFM_API_KEY` 89 | * `LASTFM_USERNAME` 90 | * `LASTFM_UPDATE_STREAM_FREQ_MINUTES` 91 | * `LASTFM_UPDATE_FREQ_MINUTES` 92 | 93 | If you didn't integrate with Last.fm enter: 94 | 95 | * `LASTFM_INTEGRATION_DISABLED` to `true` 96 | 97 | --- 98 | 99 | If you integrated with YouTube enter: 100 | 101 | * `YOUTUBE_CLIENT_ID` 102 | * `YOUTUBE_CLIENT_SECRET` 103 | * `YOUTUBE_ACCESS_TOKEN` 104 | * `YOUTUBE_REFRESH_TOKEN` 105 | * `YOUTUBE_PLAYLIST_ID` 106 | * `YOUTUBE_UPDATE_FREQ_MINUTES` 107 | 108 | If you didn't integrate with YouTube enter: 109 | 110 | * `YOUTUBE_INTEGRATION_DISABLED` to `true` 111 | 112 | --- 113 | 114 | Next, we need to get the project ready to deploy. Navigate to the `client` folder and run `grunt clean --force` and then `grunt build --force` this will group and minify all of HTML, JavaScript and CSS files from the client folder and place them in the `server > dist` folder, before continuing make sure that folder is not empty. 115 | 116 | Next we need to add the heroku repo to your project and deploy the code. In the root folder of Syte2 run the following: 117 | 118 | * `heroku git:remote -a syte2` where `syte2` needs to be replaced with your Heroku application name. 119 | * `git add .` 120 | * `git commit -am "Syte initial deployment"` 121 | * `git push heroku master` 122 | 123 | After that is done and you if have successfully deployed the code go to `http://APPNAME.herokuapp.com/stream/setup` (replace APPNAME with your heroku application name). This will download all the integrations' data to the ***mLab - MongoDB*** database. If everything went well you should be able to go to `http://APPNAME.herokuapp.com` and the app should be running. 124 | 125 | If everything went well here are a few things you should do: 126 | 127 | * Set `SETUP_ENABLED` to `false` in the Heroku's **Config Variables** 128 | * Setup [your custom domain name](https://devcenter.heroku.com/articles/custom-domains) 129 | 130 | ***I also suggest*** you to upgrade to the [Hobby](https://www.heroku.com/pricing) level of Heroku, so your website is up all day, otherwise on the `Free` level your website will go to "sleep" after 30 mins of inactivity, and every time it wakes up it will take some time. 131 | -------------------------------------------------------------------------------- /docs/images/dribbble.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/dribbble.jpg -------------------------------------------------------------------------------- /docs/images/foursquare.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/foursquare.jpg -------------------------------------------------------------------------------- /docs/images/github.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/github.jpg -------------------------------------------------------------------------------- /docs/images/instagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/instagram.jpg -------------------------------------------------------------------------------- /docs/images/lastfm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/lastfm.jpg -------------------------------------------------------------------------------- /docs/images/responsive.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/responsive.jpg -------------------------------------------------------------------------------- /docs/images/stream.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/stream.jpg -------------------------------------------------------------------------------- /docs/images/tumblr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/tumblr.jpg -------------------------------------------------------------------------------- /docs/images/twitter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/twitter.jpg -------------------------------------------------------------------------------- /docs/images/youtube.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rigoneri/Syte2/146cea7b1b5588f12a197c76ed0a3d0d7d6ee727/docs/images/youtube.jpg -------------------------------------------------------------------------------- /docs/instagram.md: -------------------------------------------------------------------------------- 1 | ### Setting up Instagram 2 | 3 | To get started, create a new application on Instagram for your website by going to . 4 | 5 | Enter the **Application Name**, **Description**, **Website URL** and **Valid Redirect URIs**. For the redirect URL enter `http://localhost:3000/instagram/auth`. Once you are done registering you will be given a **Client ID** and **Client Secret**. 6 | 7 | Once you have those four items open the `server > .env` file and enter: 8 | 9 | * **Client ID** string under `INSTAGRAM_CLIENT_ID` 10 | * **Client Secret** string under `INSTAGRAM_CLIENT_SECRET` 11 | 12 | Also make sure to set: 13 | 14 | * `INSTAGRAM_INTEGRATION_DISABLED` to false 15 | * `INSTAGRAM_OAUTH_ENABLED` to true 16 | * `INSTAGRAM_UPDATE_FREQ_MINUTES` to the number of minutes you want to wait before updating the data. `Note` A small number is not always the best and it might make Syte2 slow, use a number close to how often you use Instagram. 17 | 18 | Next, restart the server and open the following url , you will be taken to Instagram's website and will be asked to sign in and authorize your application. After you authorized you will be taken back to Syte2 and you will be given your **Access Token**. Open the `server > .env` file again and enter: 19 | 20 | * **Access Token** under `INSTAGRAM_ACCESS_TOKEN` 21 | 22 | Next, restart the server and open the following url . If everything is setup right, this will fetch some of your recent posts and save them to the database. If you have posted something recently, you should be able to go to and see that post in your stream and at this point your Instagram page should be working. 23 | 24 | ***Note: As of June 1, 2016 personal websites are in Sandbox Mode, which means that only 20 of your recent posts will fetched*** 25 | 26 | --- 27 | 28 | Next: [Setting up YouTube](youtube.md) 29 | -------------------------------------------------------------------------------- /docs/lastfm.md: -------------------------------------------------------------------------------- 1 | ## Setting up Spotify with Last.fm 2 | 3 | The Spotify integration is done via Last.fm since Spotify doesn't have the API we need. So before we can setup the integration you first need a to [Sign up to Last.fm](http://www.last.fm/) then on Spotify go to **Preferences** and under *Social* connect with Last.fm. After that's done every time a song is played Spotify will send that track information to Last.fm. So now, start playing a few songs and after a little while check if those songs are appearing in your Last.fm profile. 4 | 5 | Now that you have some data on Last.fm we need to get an **API_KEY**. Follow the [Getting started](http://www.last.fm/api) instructions and once you are done you should be able to get an **API Key** from [your api account page](http://www.last.fm/api/account). 6 | 7 | Once you have your API Key from Last.fm open the `server > .env` file and enter: 8 | 9 | * **API_KEY** under `LASTFM_API_KEY` 10 | 11 | Also make sure to set: 12 | 13 | * `LASTFM_INTEGRATION_DISABLED` to false 14 | * `LASFM_USERNAME` to your Last.fm username 15 | * `LASTFM_UPDATE_STREAM_FREQ_MINUTES` to the number of minutes you want to wait before updating the data in your stream. 16 | * `LASTFM_UPDATE_FREQ_MINUTES` to the number of minutes you want to wait before updating the data in the Spotify page 17 | 18 | `Note` A small number is not always the best and it might make Syte2 slow. If you are unsure just keep the default values. 19 | 20 | Next, restart the server and open the following url . If everything is setup right, this will fetch about 3000 of your recent played tracks, group them by day and save them to the database. If you have listened to music recently, you should be able to go to and see it in your stream and at this point your Spotify page should also be working. 21 | 22 | --- 23 | 24 | Next: [Setting up Instagram](instagram.md) 25 | -------------------------------------------------------------------------------- /docs/running.md: -------------------------------------------------------------------------------- 1 | If you haven't done so, please follow the instructions on [getting started and initial content changes](start.md) 2 | 3 | ### Running the project locally 4 | 5 | Before running the project, make sure you have MongoDB running. 6 | 7 | Your probably noticed by now that the project is split into 2 folders `client` and `server`. 8 | 9 | The client folder is where the *AnuglarJS* project lives along with all HTML and LESS(css) files. Using the terminal, navigate to the client folder and run `grunt serve`, this will covert the LESS files into CSS and validate the JavaScript. 10 | 11 | The server folder is where the *Express* project lives along with the APIs and database code. Using the terminal, navigate to the server folder and run 'node app.js', if you rather have the app to auto reload whenever you make some server changes you could run `nodemon app.js` instead. 12 | 13 | After both projects are running you should be able to run the project on however you still need to setup social integrations. 14 | 15 | * [Setting up Twitter](twitter.md) 16 | * [Setting up Github](github.md) 17 | * [Setting up Dribbble](dribbble.md) 18 | * [Setting up Spotify & Last.fm](lastfm.md) 19 | * [Setting up Instagram](instagram.md) 20 | * [Setting up YouTube](youtube.md) 21 | * [Setting up Foursquare](foursquare.md) 22 | * [Setting up Tumblr](tumblr.md) 23 | 24 | After setting up the social integrations you want. Open the `client > templates > nav.html` file and under `nav` either remove or comment out the integrations you didn't use. 25 | -------------------------------------------------------------------------------- /docs/start.md: -------------------------------------------------------------------------------- 1 | ### Getting started and initial content changes 2 | 3 | The first step is to fork Syte2. If you plan to contribute to the project and make pull request I also recommend you *branching* your fork. If you don't know what that means or how to do that just [follow these steps](https://help.github.com/articles/fork-a-repo). 4 | 5 | ## Pictures 6 | 7 | To start off change the pictures to have your picture, navigate to `client > app > images` and replace all **apple-touch-icons**, **favicon.ico** and **pic.jpg** to be your picture. Just don't replace **placeholder.png** since it's used for lazy loading images. Also make sure to keep the same sizes. 8 | 9 | ## Text changes 10 | 11 | Navigate to `client > app > index.html` and make the following changes: 12 | 13 | 1. Change `title` tag to have your name. 14 | 2. Change `meta="description"` to have a description about you. 15 | 3. Change `meta="keywords"` to have keywords about you. 16 | 17 | Then navigate to `client > app > templates > nav.html` and change the **h1** tag to have your name. 18 | 19 | ## Environment Variables 20 | 21 | Navigate to `client > app > scripts` duplicate the `sample-env.js` file and rename it to `env.js`. 22 | 23 | If you plan on using *Google Analytics* add your google analytics key to the `GOOGLE_ANALYTICS_KEY` variable. Don't worry about `GOOGLE_MAPS_KEY` for now, we will add it later. 24 | 25 | Then navigate to the `server` folder and duplicate the `.sample-env` file and rename it to `.env` 26 | 27 | ## Install Node.js and Bower 28 | 29 | First go to and download the installer to install node.js. 30 | 31 | Then open the terminal and run `npm install -g bower` to install bower. 32 | 33 | Next navigate to the `client` folder and first run `bower install` and then run `npm install` to install all the dependencies used by the client. 34 | 35 | Next navigate to the root folder and run `npm install` to install all the dependencies used by the server. 36 | 37 | `Note: Heroku requires to have package.json in the root folder, that's why it's not in the server folder` 38 | 39 | ## Install and Run MongoDB 40 | 41 | First go to and download the installer to install MongoDB. 42 | 43 | Then follow the [these instructions](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/#run-mongodb) to run MongoDB locally. 44 | 45 | ***Another option*** is to use the *mLab - MongoDB* production database you will be using later when you deploy, [see the first few paragraphs](heroku.md) of the deployment instructions. 46 | 47 | --- 48 | 49 | Next: [Running the project locally](running.md) 50 | -------------------------------------------------------------------------------- /docs/tumblr.md: -------------------------------------------------------------------------------- 1 | ### Setting up Tumblr 2 | 3 | To get started go to to register your website. Fill in the information required, for default callback url just enter `http://localhost:3000`. Once you are done your website will be listed under with a **OAuth Consumer Key** next to it. 4 | 5 | Once you have your consumer key open the `server > .env` file and enter: 6 | 7 | * **OAuth Consumer Key** string under `TUMBLR_API_KEY` 8 | 9 | Also make sure to set: 10 | 11 | * `TUMBLR_INTEGRATION_DISABLED` to false 12 | * `TUMBLR_BLOG` to your tumblr url without the http (ex. ***rigoneri.tumblr.com***) 13 | * `TUMBLR_UPDATE_FREQ_MINUTES` to the number of minutes you want to wait before updating the data. `Note` A small number is not always the best and it might make Syte2 slow, use a number close to how often you post to Tumblr. 14 | 15 | Next, restart the server and open the following url . If everything is setup right, this will fetch some of your recent posts and save them to the database. If you have posted to tumblr recently, you should be able to go to and see it in the stream and at this point your Tumblr page should also be working. 16 | 17 | --- 18 | 19 | Next: [Heroku deployment instructions](heroku.md) 20 | -------------------------------------------------------------------------------- /docs/twitter.md: -------------------------------------------------------------------------------- 1 | ## Setting up Twitter 2 | 3 | To get started, create a new application on Twitter for your website by going to . Once you are done creating your application you will be taken to the application page, from there navigate to the *Keys and Access Tokens* page. 4 | 5 | At the top of the page it will give you a **Consumer Key** and a **Consumer Secret** that you will need. At the bottom of the page there is a link called **Create my access token** click on that link. It will auto-generate your **Access Token** and **Access Token Secret** that you will also need. 6 | 7 | Once you have those four items open the `server > .env` file and enter: 8 | 9 | * **Consumer key** string under `TWITTER_CONSUMER_KEY` 10 | * **Consumer secret** string under `TWITTER_CONSUMER_SECRET` 11 | * **Access token** string under `TWITTER_ACCESS_TOKEN_KEY` 12 | * **Access token secret** string under `TWITTER_ACCESS_TOKEN_SECRET` 13 | 14 | Also make sure to set: 15 | 16 | * `TWITTER_INTEGRATION_DISABLED` to false 17 | * `TWITTER_USERNAME` to your twitter username, without the *@* 18 | * `TWITTER_UPDATE_FREQ_MINUTES` to the number of minutes you want to wait before updating the data. `Note` A small number is not always the best and it might make Syte2 slow, use a number close to how often you tweet. 19 | 20 | Next, restart the server and open the following url . If everything is setup right, this will fetch about 300 of your recent tweets and save them to the database. If you have tweeted recently, you should be able to go to and see in your stream and at this point your Twitter page should also be working. 21 | 22 | --- 23 | 24 | Next: [Setting up Github](github.md) 25 | -------------------------------------------------------------------------------- /docs/youtube.md: -------------------------------------------------------------------------------- 1 | ### Setting up YouTube 2 | 3 | To get started, login to the Google Developers Console by going to . Navigate to `API Manager > Credentials` and create a new project, after that's done go to the [OAuth consent screen](https://console.developers.google.com/apis/credentials/consent) and enter a **Product name** (all other fields are optional). 4 | 5 | Next click on `Create credentials > OAuth client ID` and enter the following: 6 | 7 | * **Application type:** Web Application 8 | * **Name:** Syte 9 | * **Authorized JavaScript origins:** Enter ***http://localhost:3000*** and your webstie url (ex. ***http://rigoneri.com***) 10 | * **Authorized redirect URIs:** ***http://localhost:3000/youtube/auth*** 11 | 12 | Once you are done you will be given a **Client ID** and **Client Secret**. 13 | 14 | Next navigate to `API Manager > Overview` and in the search for **YouTube Data API** and enable the service, then open the `server > .env` file and enter: 15 | 16 | * **Client ID** string under `YOUTUBE_CLIENT_ID` 17 | * **Client Secret** string under `YOUTUBE_CLIENT_SECRET` 18 | 19 | Also make sure to set: 20 | 21 | * `YOUTUBE_INTEGRATION_DISABLED` to false 22 | * `YOUTUBE_OAUTH_ENABLED` to true 23 | * `YOUTUBE_UPDATE_FREQ_MINUTES` to the number of minutes you want to wait before updating the data. `Note` A small number is not always the best and it might make Syte2 slow, use a number close to how often you post a video to YouTube. 24 | 25 | Next, restart the server and open the following url , you will be taken to Google's website and will be asked to sign in and authorize your application. After you authorized you will be taken back to Syte2 and you will be given an **Access Token**, a **Refresh Token** and your **Playlist ID**. Open the `server > .env` file again and enter: 26 | 27 | * **Access Token** under `YOUTUBE_ACCESS_TOKEN` 28 | * **Refresh Token** under `YOUTUBE_REFRESH_TOKEN` 29 | * **Playlist ID** under `YOUTUBE_PLAYLIST_ID` 30 | 31 | Next, restart the server and open the following url . If everything is setup right, this will fetch some of your recent videos and save them to the database. If you have posted a video recently, you should be able to go to and see that video in your stream and at this point your YouTube page should be working. 32 | 33 | --- 34 | 35 | Next: [Setting up Foursquare](foursquare.md) 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Syte2", 3 | "version": "1.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node app.js" 7 | }, 8 | "dependencies": { 9 | "async": "^2.0.0-rc.4", 10 | "body-parser": "^1.18.3", 11 | "cookie-parser": "~1.3.5", 12 | "debug": "^2.6.9", 13 | "dotenv": "^2.0.0", 14 | "express": "4.16.4", 15 | "markdown": "^0.5.0", 16 | "memory-cache": "^0.1.5", 17 | "moment": "^2.13.0", 18 | "mongodb": "^2.1.18", 19 | "morgan": "~1.9.1", 20 | "request": "^2.72.0", 21 | "serve-favicon": "^2.5.0", 22 | "twitter": "^1.2.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/.sample-env: -------------------------------------------------------------------------------- 1 | 2 | TZ = 'America/Chicago' 3 | MONGODB_URI = 'mongodb://localhost:27017/syte' 4 | SETUP_ENABLED = true 5 | 6 | TWITTER_INTEGRATION_DISABLED = true 7 | TWITTER_USERNAME = '' 8 | TWITTER_CONSUMER_KEY = '' 9 | TWITTER_CONSUMER_SECRET = '' 10 | TWITTER_ACCESS_TOKEN_KEY = '' 11 | TWITTER_ACCESS_TOKEN_SECRET = '' 12 | TWITTER_UPDATE_FREQ_MINUTES = 360 13 | 14 | INSTAGRAM_INTEGRATION_DISABLED = true 15 | INSTAGRAM_OAUTH_ENABLED = true 16 | INSTAGRAM_ACCESS_TOKEN = '' 17 | INSTAGRAM_CLIENT_ID = '' 18 | INSTAGRAM_CLIENT_SECRET = '' 19 | INSTAGRAM_UPDATE_FREQ_MINUTES = 360 20 | 21 | FOURSQUARE_INTEGRATION_DISABLED = true 22 | FOURSQUARE_OAUTH_ENABLED = true 23 | FOURSQUARE_CLIENT_ID = '' 24 | FOURSQUARE_CLIENT_SECRET = '' 25 | FOURSQUARE_ACCESS_TOKEN = '' 26 | FOURSQUARE_UPDATE_FREQ_MINUTES = 360 27 | 28 | DRIBBBLE_INTEGRATION_DISABLED = true 29 | DRIBBBLE_OAUTH_ENABLED = true 30 | DRIBBBLE_CLIENT_ID = '' 31 | DRIBBBLE_CLIENT_SECRET = '' 32 | DRIBBBLE_ACCESS_TOKEN = '' 33 | DRIBBBLE_UPDATE_FREQ_MINUTES = 720 34 | 35 | TUMBLR_INTEGRATION_DISABLED = true 36 | TUMBLR_BLOG = '' 37 | TUMBLR_API_KEY = '' 38 | TUMBLR_UPDATE_FREQ_MINUTES = 1440 39 | 40 | GITHUB_INTEGRATION_DISABLED = true 41 | GITHUB_OAUTH_ENABLED = true 42 | GITHUB_CLIENT_ID = '' 43 | GITHUB_CLIENT_SECRET = '' 44 | GITHUB_ACCESS_TOKEN = '' 45 | GITHUB_USERNAME = '' 46 | GITHUB_UPDATE_FREQ_MINUTES = 720 47 | 48 | LASTFM_INTEGRATION_DISABLED = true 49 | LASTFM_API_KEY = '' 50 | LASTFM_USERNAME = '' 51 | LASTFM_UPDATE_STREAM_FREQ_MINUTES = 720 52 | LASTFM_UPDATE_FREQ_MINUTES = 60 53 | 54 | YOUTUBE_INTEGRATION_DISABLED = true 55 | YOUTUBE_OAUTH_ENABLED = true 56 | YOUTUBE_CLIENT_ID = '' 57 | YOUTUBE_CLIENT_SECRET = '' 58 | YOUTUBE_ACCESS_TOKEN = '' 59 | YOUTUBE_REFRESH_TOKEN = '' 60 | YOUTUBE_PLAYLIST_ID = '' 61 | YOUTUBE_UPDATE_FREQ_MINUTES = 4320 62 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var http = require('http'); 3 | var path = require('path'); 4 | //var favicon = require('serve-favicon'); 5 | var logger = require('morgan'); 6 | var cookieParser = require('cookie-parser'); 7 | var bodyParser = require('body-parser'); 8 | var db = require('./db'); 9 | require('dotenv').config(); 10 | 11 | var app = express(); 12 | var server = http.createServer(app); 13 | var port = normalizePort(process.env.PORT || '3000'); 14 | app.set('port', port); 15 | 16 | // uncomment after placing your favicon in /public 17 | // app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 18 | app.use(logger('dev')); 19 | app.use(bodyParser.json()); 20 | app.use(bodyParser.urlencoded({ extended: false })); 21 | app.use(cookieParser()); 22 | app.use(require('./controllers')) 23 | 24 | app.set('mongodbURL', process.env.MONGODB_URI); 25 | 26 | if (app.get('env') === 'development') { 27 | // This will change in production since we'll be using the dist folder 28 | app.use(express.static(path.join(__dirname, '../client'))); 29 | // This covers serving up the index page 30 | app.use(express.static(path.join(__dirname, '../client/.tmp'))); 31 | app.use(express.static(path.join(__dirname, '../client/app'))); 32 | 33 | // Error Handling 34 | app.use(function(err, req, res, next) { 35 | res.status(err.status || 500); 36 | res.render('error', { 37 | message: err.message, 38 | error: err 39 | }); 40 | }); 41 | } 42 | 43 | if (app.get('env') === 'production') { 44 | // changes it to use the optimized version for production 45 | app.use(express.static(path.join(__dirname, '/dist'))); 46 | 47 | // production error handler 48 | // no stacktraces leaked to user 49 | app.use(function(err, req, res, next) { 50 | res.status(err.status || 500); 51 | res.render('error', { 52 | message: err.message, 53 | error: {} 54 | }); 55 | }); 56 | } 57 | 58 | db.connect(app.settings.mongodbURL, function(err) { 59 | if (err) { 60 | console.log('Unable to connect to Mongo.'); 61 | process.exit(1) 62 | } else { 63 | console.log('Connected to Mongo.', app.get('env')); 64 | server.listen(port); 65 | server.on('error', onError); 66 | server.on('listening', onListening); 67 | } 68 | }); 69 | 70 | function normalizePort(val) { 71 | var port = parseInt(val, 10); 72 | 73 | if (isNaN(port)) { 74 | // named pipe 75 | return val; 76 | } 77 | 78 | if (port >= 0) { 79 | // port number 80 | return port; 81 | } 82 | 83 | return false; 84 | } 85 | 86 | function onError(error) { 87 | if (error.syscall !== 'listen') { 88 | throw error; 89 | } 90 | 91 | var bind = typeof port === 'string' 92 | ? 'Pipe ' + port 93 | : 'Port ' + port; 94 | 95 | // handle specific listen errors with friendly messages 96 | switch (error.code) { 97 | case 'EACCES': 98 | console.error(bind + ' requires elevated privileges'); 99 | process.exit(1); 100 | break; 101 | case 'EADDRINUSE': 102 | console.error(bind + ' is already in use'); 103 | process.exit(1); 104 | break; 105 | default: 106 | throw error; 107 | } 108 | } 109 | 110 | function onListening() { 111 | var addr = server.address(); 112 | var bind = typeof addr === 'string' 113 | ? 'pipe ' + addr 114 | : 'port ' + addr.port; 115 | console.log('Listening on ' + bind); 116 | } 117 | 118 | module.exports = app; 119 | -------------------------------------------------------------------------------- /server/controllers/dribbble.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | router = express.Router(), 3 | Dribbble = require('../models/dribbble'); 4 | 5 | var DRIBBBLE_AUTH_URL = 'https://dribbble.com/oauth/authorize', 6 | DRIBBBLE_AUTH_REDIRECT_URL = 'http://localhost:3000/dribbble/auth'; 7 | 8 | router.get('/', function(req, res) { 9 | Dribbble.monthActivity(0, function(error, data) { 10 | if (!error) { 11 | res.status(200).json(data); 12 | } 13 | }); 14 | }); 15 | 16 | router.get('/setup', function(req, res) { 17 | if (process.env.SETUP_ENABLED != 'true') { 18 | res.status(404).send('Not found'); 19 | return; 20 | } 21 | 22 | Dribbble.setup(function(error, data) { 23 | res.status(200).send(error ? 'Setup failed see logs': 'Setup done!'); 24 | }); 25 | }); 26 | 27 | 28 | router.get('/auth', function(req, res) { 29 | if (process.env.DRIBBBLE_OAUTH_ENABLED != 'true') { 30 | res.status(404).send('Not found'); 31 | return; 32 | } 33 | 34 | var code = req.query.code; 35 | if (code) { 36 | Dribbble.getToken(code, function(response) { 37 | res.status(200).json(response); 38 | }); 39 | } else { 40 | var url = DRIBBBLE_AUTH_URL + '?client_id=' + process.env.DRIBBBLE_CLIENT_ID + 41 | '&redirect_uri=' + DRIBBBLE_AUTH_REDIRECT_URL + '&response_type=code'; 42 | res.redirect(url); 43 | } 44 | }); 45 | 46 | 47 | router.get('/user', function(req, res) { 48 | Dribbble.user(function(error, data) { 49 | res.status(200).json(data); 50 | }); 51 | }); 52 | 53 | router.get('/:page', function(req, res) { 54 | var page = parseInt(req.params.page); 55 | if (!page) 56 | page = 0; 57 | 58 | Dribbble.recentActivity(page, function(error, data) { 59 | if (!error) { 60 | res.status(200).json(data); 61 | } 62 | }); 63 | }); 64 | 65 | module.exports = router; 66 | -------------------------------------------------------------------------------- /server/controllers/foursquare.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | router = express.Router(), 3 | Foursquare = require('../models/foursquare'); 4 | 5 | var FOURSQUARE_AUTH_URL = 'https://foursquare.com/oauth2/authorize', 6 | FOURSQUARE_AUTH_REDIRECT_URL = 'http://localhost:3000/foursquare/auth'; 7 | 8 | router.get('/', function(req, res) { 9 | Foursquare.monthActivity(0, function(error, data) { 10 | if (!error) { 11 | res.status(200).json(data); 12 | } 13 | }); 14 | }); 15 | 16 | router.get('/setup', function(req, res) { 17 | if (process.env.SETUP_ENABLED != 'true') { 18 | res.status(404).send('Not found'); 19 | return; 20 | } 21 | 22 | Foursquare.setup(function(error, data) { 23 | res.status(200).send(error ? 'Setup failed see logs': 'Setup done!'); 24 | }); 25 | }); 26 | 27 | router.get('/auth', function(req, res) { 28 | if (process.env.FOURSQUARE_OAUTH_ENABLED != 'true') { 29 | res.status(404).send('Not found'); 30 | return; 31 | } 32 | 33 | var code = req.query.code; 34 | if (code) { 35 | Foursquare.getToken(code, function(response) { 36 | res.status(200).json(response); 37 | }); 38 | } else { 39 | var url = FOURSQUARE_AUTH_URL + '?client_id=' + process.env.FOURSQUARE_CLIENT_ID + 40 | '&redirect_uri=' + FOURSQUARE_AUTH_REDIRECT_URL + 41 | '&response_type=code'; 42 | res.redirect(url); 43 | } 44 | }); 45 | 46 | router.get('/user', function(req, res) { 47 | Foursquare.user(function(error, data) { 48 | if (!error) { 49 | res.status(200).json(data); 50 | } 51 | }); 52 | }); 53 | 54 | router.get('/:page', function(req, res) { 55 | var page = parseInt(req.params.page); 56 | if (!page) 57 | page = 0; 58 | 59 | Foursquare.monthActivity(page, function(error, data) { 60 | if (!error) { 61 | res.status(200).json(data); 62 | } 63 | }); 64 | }); 65 | 66 | module.exports = router; 67 | -------------------------------------------------------------------------------- /server/controllers/github.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | router = express.Router(), 3 | Github = require('../models/github'); 4 | 5 | var GITHUB_AUTH_URL = 'https://github.com/login/oauth/authorize', 6 | GITHUB_AUTH_REDIRECT_URL = 'http://localhost:3000/github/auth'; 7 | 8 | router.get('/', function(req, res) { 9 | Github.monthActivity(0, function(error, data) { 10 | if (!error) { 11 | res.status(200).json(data); 12 | } 13 | }); 14 | }); 15 | 16 | router.get('/setup', function(req, res) { 17 | if (process.env.SETUP_ENABLED != 'true') { 18 | res.status(404).send('Not found'); 19 | return; 20 | } 21 | 22 | Github.setup(function(error, data) { 23 | res.status(200).send(error ? 'Setup failed see logs': 'Setup done!'); 24 | }); 25 | }); 26 | 27 | router.get('/auth', function(req, res) { 28 | if (process.env.GITHUB_OAUTH_ENABLED != 'true') { 29 | res.status(404).send('Not found'); 30 | return; 31 | } 32 | 33 | var code = req.query.code; 34 | if (code) { 35 | Github.getToken(code, function(response) { 36 | res.status(200).json(response); 37 | }); 38 | } else { 39 | var url = GITHUB_AUTH_URL + '?client_id=' + process.env.GITHUB_CLIENT_ID + 40 | '&redirect_uri=' + GITHUB_AUTH_REDIRECT_URL + '&response_type=code'; 41 | res.redirect(url); 42 | } 43 | }); 44 | 45 | router.get('/user', function(req, res) { 46 | Github.user(function(error, data) { 47 | if (!error) { 48 | res.status(200).json(data); 49 | } 50 | }); 51 | }); 52 | 53 | router.get('/repos', function(req, res) { 54 | Github.repos(function(error, data) { 55 | if (!error) { 56 | res.status(200).json(data); 57 | } 58 | }); 59 | }); 60 | 61 | router.get('/activity', function(req, res) { 62 | Github.recentActivity(function(error, data) { 63 | if (!error) { 64 | res.status(200).json(data); 65 | } 66 | }); 67 | }); 68 | 69 | 70 | module.exports = router; 71 | -------------------------------------------------------------------------------- /server/controllers/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | router = express.Router(); 3 | 4 | router.use('/stream', require('./stream')); 5 | 6 | if (process.env.TWITTER_INTEGRATION_DISABLED != 'true') { 7 | router.use('/twitter', require('./twitter')); 8 | } 9 | 10 | if (process.env.INSTAGRAM_INTEGRATION_DISABLED != 'true') { 11 | router.use('/instagram', require('./instagram')); 12 | } 13 | 14 | if (process.env.FOURSQUARE_INTEGRATION_DISABLED != 'true') { 15 | router.use('/foursquare', require('./foursquare')); 16 | } 17 | 18 | if (process.env.DRIBBBLE_INTEGRATION_DISABLED != 'true') { 19 | router.use('/dribbble', require('./dribbble')); 20 | } 21 | 22 | if (process.env.TUMBLR_INTEGRATION_DISABLED != 'true') { 23 | router.use('/tumblr', require('./tumblr')); 24 | router.get('/post/:postId', function(req, res) { 25 | res.redirect('/#/post/' + req.params.postId); 26 | }); 27 | } 28 | 29 | if (process.env.GITHUB_INTEGRATION_DISABLED != 'true') { 30 | router.use('/github', require('./github')); 31 | } 32 | 33 | if (process.env.LASTFM_INTEGRATION_DISABLED != 'true') { 34 | router.use('/lastfm', require('./lastfm')); 35 | } 36 | 37 | if (process.env.YOUTUBE_INTEGRATION_DISABLED != 'true') { 38 | router.use('/youtube', require('./youtube')); 39 | } 40 | 41 | module.exports = router; 42 | -------------------------------------------------------------------------------- /server/controllers/instagram.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | router = express.Router(), 3 | Instagram = require('../models/instagram'); 4 | 5 | var INSTAGRAM_AUTH_URL = 'https://api.instagram.com/oauth/authorize', 6 | INSTAGRAM_AUTH_REDIRECT_URL = 'http://localhost:3000/instagram/auth'; 7 | 8 | router.get('/', function(req, res) { 9 | Instagram.monthActivity(0, function(error, data) { 10 | if (!error) { 11 | res.status(200).json(data); 12 | } 13 | }); 14 | }); 15 | 16 | router.get('/recent', function(req, res) { 17 | Instagram.recentActivity(function(error, data) { 18 | if (!error) { 19 | res.status(200).json(data); 20 | } 21 | }); 22 | }); 23 | 24 | router.get('/setup', function(req, res) { 25 | if (process.env.SETUP_ENABLED != 'true') { 26 | res.status(404).send('Not found'); 27 | return; 28 | } 29 | 30 | Instagram.setup(function(error, data) { 31 | res.status(200).send(error ? 'Setup failed see logs': 'Setup done!'); 32 | }); 33 | }); 34 | 35 | router.get('/auth', function(req, res) { 36 | if (process.env.INSTAGRAM_OAUTH_ENABLED != 'true') { 37 | res.status(404).send('Not found'); 38 | return; 39 | } 40 | 41 | var code = req.query.code; 42 | if (code) { 43 | Instagram.getToken(code, function(response) { 44 | res.status(200).json(response); 45 | }); 46 | } else { 47 | var url = INSTAGRAM_AUTH_URL + '?client_id=' + process.env.INSTAGRAM_CLIENT_ID + 48 | '&redirect_uri=' + INSTAGRAM_AUTH_REDIRECT_URL + '&response_type=code'; 49 | res.redirect(url); 50 | } 51 | }); 52 | 53 | router.get('/user', function(req, res) { 54 | Instagram.user(function(error, data) { 55 | res.status(200).json(data); 56 | }); 57 | }); 58 | 59 | router.get('/:page', function(req, res) { 60 | var page = parseInt(req.params.page); 61 | if (!page) 62 | page = 0; 63 | 64 | Instagram.monthActivity(page, function(error, data) { 65 | if (!error) { 66 | res.status(200).json(data); 67 | } 68 | }); 69 | }); 70 | 71 | module.exports = router; 72 | -------------------------------------------------------------------------------- /server/controllers/lastfm.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | router = express.Router(), 3 | Lastfm = require('../models/lastfm'); 4 | 5 | router.get('/', function(req, res) { 6 | Lastfm.monthActivity(0, function(error, data) { 7 | if (!error) { 8 | res.status(200).json(data); 9 | } 10 | }); 11 | }); 12 | 13 | router.get('/setup', function(req, res) { 14 | if (process.env.SETUP_ENABLED != 'true') { 15 | res.status(404).send('Not found'); 16 | return; 17 | } 18 | 19 | Lastfm.setup(function(error, data) { 20 | res.status(200).send(error ? 'Setup failed see logs': 'Setup done!'); 21 | }); 22 | }); 23 | 24 | router.get('/user', function(req, res) { 25 | Lastfm.user(function(error, data) { 26 | res.status(200).json(data); 27 | }); 28 | }); 29 | 30 | router.get('/activity', function(req, res) { 31 | Lastfm.recentActivity(function(error, data) { 32 | if (!error) { 33 | res.status(200).json(data); 34 | } 35 | }); 36 | }); 37 | 38 | router.get('/top', function(req, res) { 39 | Lastfm.topActivity(function(error, data) { 40 | if (!error) { 41 | res.status(200).json(data); 42 | } 43 | }); 44 | }); 45 | 46 | 47 | module.exports = router; 48 | -------------------------------------------------------------------------------- /server/controllers/stream.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | router = express.Router(), 3 | async = require('async'), 4 | Twitter = require('../models/twitter'), 5 | Instagram = require('../models/instagram'), 6 | Foursquare = require('../models/foursquare'), 7 | Dribbble = require('../models/dribbble'), 8 | Tumblr = require('../models/tumblr'), 9 | Github = require('../models/github'), 10 | Lastfm = require('../models/lastfm'), 11 | YouTube = require('../models/youtube'); 12 | 13 | var streamPosts; 14 | 15 | router.get('/setup', function(req, res) { 16 | if (process.env.SETUP_ENABLED != 'true') { 17 | res.status(404).send('Not found'); 18 | return; 19 | } 20 | 21 | async.series([ 22 | function(cb) { 23 | Twitter.setup(cb); 24 | }, 25 | function(cb) { 26 | Instagram.setup(cb); 27 | }, 28 | function(cb) { 29 | Dribbble.setup(cb); 30 | }, 31 | function(cb) { 32 | Foursquare.setup(cb); 33 | }, 34 | function(cb) { 35 | Tumblr.setup(cb); 36 | }, 37 | function(cb) { 38 | Github.setup(cb); 39 | }, 40 | function(cb) { 41 | Lastfm.setup(cb); 42 | }, 43 | function(cb) { 44 | YouTube.setup(cb); 45 | } 46 | ], function(err, results) { 47 | if (!err) { 48 | res.status(200).send(err ? 'Setup failed see logs': 'Setup done!'); 49 | } 50 | }); 51 | }); 52 | 53 | router.get('/:page', function(req, res) { 54 | var page = parseInt(req.params.page); 55 | if (!page) 56 | page = 0; 57 | 58 | async.series([ 59 | function(cb) { 60 | Twitter.monthActivity(page, cb); 61 | }, 62 | function(cb) { 63 | Instagram.monthActivity(page, cb); 64 | }, 65 | function(cb) { 66 | Dribbble.monthActivity(page, cb); 67 | }, 68 | function(cb) { 69 | Foursquare.monthActivity(page, cb); 70 | }, 71 | function(cb) { 72 | Tumblr.monthActivity(page, cb); 73 | }, 74 | function(cb) { 75 | Github.monthActivity(page, cb); 76 | }, 77 | function(cb) { 78 | Lastfm.monthActivity(page, cb); 79 | }, 80 | function(cb) { 81 | YouTube.monthActivity(page, cb); 82 | } 83 | ], function(err, results) { 84 | if (!err) { 85 | streamPosts = []; 86 | for (var i=0; i Rigo [Rodrigo Neri]
-------------------------------------------------------------------------------- /server/dist/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | Disallow: 5 | -------------------------------------------------------------------------------- /server/utils/dates.js: -------------------------------------------------------------------------------- 1 | var moment = require('moment'); 2 | 3 | exports.monthRange = function(page, cb) { 4 | var today = moment(); 5 | if (page > 0) { 6 | today.subtract(page, 'months'); 7 | } 8 | 9 | var startDate = today.startOf('month'); 10 | var endDate = moment(startDate).endOf('month'); 11 | 12 | cb(startDate.toISOString(), endDate.toISOString()); 13 | }; 14 | 15 | 16 | exports.lastYearRange = function(cb) { 17 | var startDate = moment().subtract(1, 'years'); 18 | var endDate = moment(); 19 | 20 | cb(startDate.toISOString(), endDate.toISOString()); 21 | }; 22 | 23 | --------------------------------------------------------------------------------