├── www ├── CNAME ├── js │ ├── privacy.js │ └── index.js └── css │ └── privacy.css ├── Procfile ├── browserslist ├── .npmignore ├── .env.default ├── assets ├── images │ ├── logo.png │ ├── cursor.png │ ├── djkhaled.jpg │ ├── loading.gif │ ├── logo-128.png │ ├── logo-16.png │ ├── logo-19.png │ ├── logo-38.png │ ├── rrhoover.jpg │ ├── browser-one.jpg │ ├── browser-two.jpg │ ├── carlitowhite.png │ ├── facebookshare.png │ ├── missing-image.png │ ├── twittershare.png │ ├── carlito-corner.png │ ├── facebeefbanner.jpg │ ├── logo-16-active.png │ ├── logo-19-active.png │ ├── logo-38-active.png │ ├── 2B8CECF41AECD732.png │ ├── blue-browser-hc1.gif │ ├── hovercards-error.png │ ├── inject-the-binary.jpg │ ├── purp-browser-hc1.png │ ├── purp-browser-hc22.gif │ ├── purp-browser-hc3.png │ ├── blue-browser-hc1-bg.png │ ├── Imgur-icon-full_color.png │ ├── Reddit-icon-full_color.png │ ├── Twitter-icon-full_color.png │ ├── YouTube-icon-full_color.png │ ├── blue-browser-lightbox.gif │ ├── imgur-icon-full_color.png │ ├── purp-browser-hc-shadow.png │ ├── reddit-icon-full_color.png │ ├── twitter-icon-full_color.png │ ├── youtube-icon-full_color.png │ ├── blue-browser-lightbox-bg.png │ ├── facebook-icon-full_color.png │ ├── instagram-icon-full_color.png │ ├── SoundCloud-icon-full_color.png │ ├── soundcloud-icon-full_color.png │ ├── fullscreen.svg │ ├── twitter-retweet.svg │ ├── close-button.svg │ ├── twitter.svg │ ├── twitter-black.svg │ ├── instagram.svg │ ├── youtube.svg │ ├── facebook-black.svg │ ├── circles.svg │ ├── right-arrow.svg │ ├── left-arrow.svg │ ├── verified.svg │ ├── imgur.svg │ ├── soundcloud.svg │ └── reddit.svg └── fonts │ ├── avant-garde.eot │ ├── avant-garde.ttf │ ├── NeuzeGroTReg.eot │ ├── NeuzeGroTReg.woff │ └── avant-garde.woff ├── redux ├── actions.options.js ├── actions.top-frame.js ├── actions.background.js ├── createStore.options.js ├── authentication.actions.top-frame.js ├── createStore.background.js ├── createStore.top-frame.js ├── options.actions.js ├── analytics.actions.top-frame.js ├── entities.reducer.js ├── entities.actions.top-frame.js ├── createStore.common.js ├── authentication.reducer.js ├── storelistener.js ├── authentication.actions.background.js ├── options.reducer.js ├── analytics.actions.background.js └── entities.actions.background.js ├── components ├── ContentHovercard │ ├── ContentHovercard.styles.css │ └── ContentHovercard.js ├── Gif │ ├── Gif.styles.css │ └── Gif.js ├── Image │ ├── Image.styles.css │ └── Image.js ├── Video │ ├── Video.styles.css │ └── Video.js ├── YoutubeVideo │ ├── YoutubeVideo.styles.css │ └── YoutubeVideo.js ├── SoundCloudPlayer │ ├── SoundCloudPlayer.styles.css │ └── SoundCloudPlayer.js ├── OEmbed │ ├── OEmbed.styles.css │ └── OEmbed.js ├── ContentDescription │ ├── ContentDescription.styles.css │ └── ContentDescription.js ├── flex.styles.css ├── AccountHeader │ ├── AccountHeader.styles.css │ └── AccountHeader.js ├── Loading │ ├── Loading.styles.css │ └── Loading.js ├── Hovercard │ └── Hovercard.styles.css ├── Media │ ├── Media.styles.css │ └── Media.js ├── Discussions │ └── Discussions.styles.css ├── meta.styles.css ├── IntegrationOptions │ ├── IntegrationOptions.styles.css │ └── IntegrationOptions.js ├── Carousel │ ├── Carousel.styles.css │ └── Carousel.js ├── ContentHeader │ ├── ContentHeader.styles.css │ └── ContentHeader.js ├── Options │ ├── Options.styles.css │ └── Options.js ├── AccountFooter │ ├── AccountFooter.styles.css │ └── AccountFooter.js ├── Collapsable │ ├── Collapsable.styles.css │ └── Collapsable.js ├── AccountHovercard │ ├── AccountHovercard.styles.css │ └── AccountHovercard.js ├── DiscussionComment │ ├── DiscussionComment.styles.css │ └── DiscussionComment.js ├── ContentFooter │ └── ContentFooter.js ├── TimeSince │ └── TimeSince.js ├── Err │ └── Err.js └── no-formatting.styles.css ├── .babelrc ├── .gitignore ├── AUTHORS ├── server ├── redis.js ├── index.js └── v2.js ├── Procfile.dev ├── report ├── index.js ├── index.www.js └── index.browser.js ├── extension ├── index.background.js ├── index.options.js ├── index.top-frame.js ├── config.js ├── set-uninstall-url.js ├── content-security-policy.js ├── manifest.json ├── browser-action.js ├── browser.js └── copy.json ├── .travis.yml ├── integrations ├── mixins.js ├── twitter │ ├── config.json │ └── urls.js ├── reddit │ ├── config.json │ └── urls.js ├── config.js ├── imgur │ ├── config.json │ └── urls.js ├── soundcloud │ ├── config.json │ └── urls.js ├── youtube │ ├── config.json │ └── urls.js ├── instagram │ ├── config.js │ ├── urls.js │ └── urls.test.js ├── urls │ ├── index.js │ └── index.test.js ├── index.js └── index.background.js ├── utils ├── entity-label.js ├── format.js └── dom.js ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── CONTRIBUTING.md ├── bower.json ├── .stylelintrc ├── .eslintrc ├── app.json ├── webpack.config.www.js ├── webpack.config.extension.js └── README.md /www/CNAME: -------------------------------------------------------------------------------- 1 | hovercards.com 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node server 2 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | Chrome >= 43 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !dist-* 3 | !dist/* 4 | -------------------------------------------------------------------------------- /www/js/privacy.js: -------------------------------------------------------------------------------- 1 | require('../../report'); 2 | require('../css/privacy.css'); 3 | -------------------------------------------------------------------------------- /.env.default: -------------------------------------------------------------------------------- 1 | INSTAGRAM_CLIENT_ID= 2 | REDDIT_CLIENT_ID= 3 | SOUNDCLOUD_CLIENT_ID= 4 | -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/logo.png -------------------------------------------------------------------------------- /assets/images/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/cursor.png -------------------------------------------------------------------------------- /assets/images/djkhaled.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/djkhaled.jpg -------------------------------------------------------------------------------- /assets/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/loading.gif -------------------------------------------------------------------------------- /assets/images/logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/logo-128.png -------------------------------------------------------------------------------- /assets/images/logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/logo-16.png -------------------------------------------------------------------------------- /assets/images/logo-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/logo-19.png -------------------------------------------------------------------------------- /assets/images/logo-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/logo-38.png -------------------------------------------------------------------------------- /assets/images/rrhoover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/rrhoover.jpg -------------------------------------------------------------------------------- /assets/fonts/avant-garde.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/fonts/avant-garde.eot -------------------------------------------------------------------------------- /assets/fonts/avant-garde.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/fonts/avant-garde.ttf -------------------------------------------------------------------------------- /assets/fonts/NeuzeGroTReg.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/fonts/NeuzeGroTReg.eot -------------------------------------------------------------------------------- /assets/fonts/NeuzeGroTReg.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/fonts/NeuzeGroTReg.woff -------------------------------------------------------------------------------- /assets/fonts/avant-garde.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/fonts/avant-garde.woff -------------------------------------------------------------------------------- /assets/images/browser-one.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/browser-one.jpg -------------------------------------------------------------------------------- /assets/images/browser-two.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/browser-two.jpg -------------------------------------------------------------------------------- /assets/images/carlitowhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/carlitowhite.png -------------------------------------------------------------------------------- /assets/images/facebookshare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/facebookshare.png -------------------------------------------------------------------------------- /assets/images/missing-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/missing-image.png -------------------------------------------------------------------------------- /assets/images/twittershare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/twittershare.png -------------------------------------------------------------------------------- /redux/actions.options.js: -------------------------------------------------------------------------------- 1 | module.exports = Object.assign( 2 | {}, 3 | require('./options.actions') 4 | ); 5 | -------------------------------------------------------------------------------- /assets/images/carlito-corner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/carlito-corner.png -------------------------------------------------------------------------------- /assets/images/facebeefbanner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/facebeefbanner.jpg -------------------------------------------------------------------------------- /assets/images/logo-16-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/logo-16-active.png -------------------------------------------------------------------------------- /assets/images/logo-19-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/logo-19-active.png -------------------------------------------------------------------------------- /assets/images/logo-38-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/logo-38-active.png -------------------------------------------------------------------------------- /assets/images/2B8CECF41AECD732.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/2B8CECF41AECD732.png -------------------------------------------------------------------------------- /assets/images/blue-browser-hc1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/blue-browser-hc1.gif -------------------------------------------------------------------------------- /assets/images/hovercards-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/hovercards-error.png -------------------------------------------------------------------------------- /assets/images/inject-the-binary.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/inject-the-binary.jpg -------------------------------------------------------------------------------- /assets/images/purp-browser-hc1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/purp-browser-hc1.png -------------------------------------------------------------------------------- /assets/images/purp-browser-hc22.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/purp-browser-hc22.gif -------------------------------------------------------------------------------- /assets/images/purp-browser-hc3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/purp-browser-hc3.png -------------------------------------------------------------------------------- /assets/images/blue-browser-hc1-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/blue-browser-hc1-bg.png -------------------------------------------------------------------------------- /components/ContentHovercard/ContentHovercard.styles.css: -------------------------------------------------------------------------------- 1 | .content { 2 | min-height: 140px; 3 | min-width: 620px; 4 | } 5 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react"], 3 | "plugins": ["transform-es2015-computed-properties", "transform-object-assign"] 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | coverage/* 3 | dist-* 4 | dist/* 5 | dump.rdb 6 | bower_components/* 7 | node_modules/* 8 | npm-debug.log* 9 | -------------------------------------------------------------------------------- /assets/images/Imgur-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/Imgur-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/Reddit-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/Reddit-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/Twitter-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/Twitter-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/YouTube-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/YouTube-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/blue-browser-lightbox.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/blue-browser-lightbox.gif -------------------------------------------------------------------------------- /assets/images/imgur-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/imgur-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/purp-browser-hc-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/purp-browser-hc-shadow.png -------------------------------------------------------------------------------- /assets/images/reddit-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/reddit-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/twitter-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/twitter-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/youtube-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/youtube-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/blue-browser-lightbox-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/blue-browser-lightbox-bg.png -------------------------------------------------------------------------------- /assets/images/facebook-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/facebook-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/instagram-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/instagram-icon-full_color.png -------------------------------------------------------------------------------- /components/Gif/Gif.styles.css: -------------------------------------------------------------------------------- 1 | .gif { 2 | display: block; 3 | margin: auto; 4 | max-width: 100%; 5 | user-select: none; 6 | } 7 | -------------------------------------------------------------------------------- /assets/images/SoundCloud-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/SoundCloud-icon-full_color.png -------------------------------------------------------------------------------- /assets/images/soundcloud-icon-full_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kogg/hovercards/HEAD/assets/images/soundcloud-icon-full_color.png -------------------------------------------------------------------------------- /components/Image/Image.styles.css: -------------------------------------------------------------------------------- 1 | .image { 2 | display: block; 3 | margin: auto; 4 | max-width: 100%; 5 | user-select: none; 6 | } 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Cameron Rohani (https://twitter.com/cameronrohani) 2 | Marco Marandiz (http://marcomarandiz.com/) 3 | Saiichi Hashimoto (http://saiichihashimoto.com/) 4 | -------------------------------------------------------------------------------- /components/Video/Video.styles.css: -------------------------------------------------------------------------------- 1 | .video { 2 | cursor: pointer; 3 | display: block; 4 | margin: auto; 5 | max-width: 100%; 6 | user-select: none; 7 | } 8 | -------------------------------------------------------------------------------- /components/YoutubeVideo/YoutubeVideo.styles.css: -------------------------------------------------------------------------------- 1 | .video { 2 | background-position: center; 3 | background-size: cover; 4 | height: 321px; 5 | width: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /components/SoundCloudPlayer/SoundCloudPlayer.styles.css: -------------------------------------------------------------------------------- 1 | .player { 2 | background-position: center; 3 | background-size: cover; 4 | height: 293px; 5 | width: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /redux/actions.top-frame.js: -------------------------------------------------------------------------------- 1 | module.exports = Object.assign( 2 | {}, 3 | require('./analytics.actions.top-frame'), 4 | require('./authentication.actions.top-frame'), 5 | require('./entities.actions.top-frame') 6 | ); 7 | -------------------------------------------------------------------------------- /redux/actions.background.js: -------------------------------------------------------------------------------- 1 | module.exports = Object.assign( 2 | {}, 3 | require('./analytics.actions.background'), 4 | require('./authentication.actions.background'), 5 | require('./entities.actions.background') 6 | ); 7 | -------------------------------------------------------------------------------- /server/redis.js: -------------------------------------------------------------------------------- 1 | var redis = require('redis'); 2 | 3 | var report = require('../report'); 4 | 5 | module.exports = redis.createClient({ url: process.env.REDISCLOUD_URL }); 6 | 7 | module.exports.on('error', report.captureException); 8 | -------------------------------------------------------------------------------- /Procfile.dev: -------------------------------------------------------------------------------- 1 | www: webpack-dev-server --inline --hot --config webpack.config.www.js 2 | web: nodemon --ignore dist/ -e js,jsx server 3 | extension: webpack-dev-server --config webpack.config.extension.js 4 | redis: redis-server 5 | -------------------------------------------------------------------------------- /report/index.js: -------------------------------------------------------------------------------- 1 | var raven = require('raven'); 2 | 3 | module.exports = new raven.Client(process.env.SENTRY_DSN, { 4 | environment: process.env.NODE_ENV, 5 | release: process.env.npm_package_version 6 | }); 7 | 8 | module.exports.patchGlobal(); 9 | -------------------------------------------------------------------------------- /report/index.www.js: -------------------------------------------------------------------------------- 1 | var Raven = require('raven-js'); 2 | 3 | Raven 4 | .config(process.env.SENTRY_DSN_CLIENT, { 5 | environment: process.env.NODE_ENV, 6 | release: process.env.npm_package_version 7 | }) 8 | .install(); 9 | 10 | module.exports = Raven; 11 | -------------------------------------------------------------------------------- /extension/index.background.js: -------------------------------------------------------------------------------- 1 | require('../report'); 2 | require('string.prototype.endswith'); 3 | var createStore = require('../redux/createStore.background'); 4 | 5 | require('./browser-action'); 6 | require('./content-security-policy'); 7 | require('./set-uninstall-url'); 8 | 9 | createStore(); 10 | -------------------------------------------------------------------------------- /components/OEmbed/OEmbed.styles.css: -------------------------------------------------------------------------------- 1 | .oembed-container { 2 | background-position: center; 3 | background-size: cover; 4 | max-width: 100%; 5 | width: 100%; 6 | } 7 | 8 | .oembed { 9 | display: block; 10 | margin: auto; 11 | max-width: 100%; 12 | user-select: none; 13 | width: 100%; 14 | } 15 | -------------------------------------------------------------------------------- /redux/createStore.options.js: -------------------------------------------------------------------------------- 1 | var actions = require('./actions.options'); 2 | var createStore = require('./createStore.common'); 3 | 4 | module.exports = function(initialState) { 5 | return createStore( 6 | actions, 7 | { 8 | options: require('./options.reducer') 9 | }, 10 | initialState 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.1" 4 | before_install: 5 | - npm prune 6 | script: 7 | - npm test 8 | - npm run coverage:check 9 | after_success: 10 | - npm run coverage:report 11 | - npm run release 12 | cache: 13 | directories: 14 | - node_modules 15 | notifications: 16 | email: false 17 | -------------------------------------------------------------------------------- /integrations/mixins.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | _.mixin({ 4 | somePredicate: function() { 5 | var predicates = arguments; 6 | return function() { 7 | var args = arguments; 8 | return _.some(predicates, function(predicate) { 9 | return predicate.apply(_, args); 10 | }); 11 | }; 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /components/ContentDescription/ContentDescription.styles.css: -------------------------------------------------------------------------------- 1 | .name { 2 | font-size: 14px; 3 | color: rgba(0, 0, 0, .8); 4 | font-weight: 600; 5 | display: block; 6 | margin-bottom: 5px; 7 | } 8 | 9 | .description { 10 | margin-top: 0; 11 | max-height: 90px; 12 | padding: 12px 24px 0 24px; 13 | width: 100%; 14 | } 15 | -------------------------------------------------------------------------------- /redux/authentication.actions.top-frame.js: -------------------------------------------------------------------------------- 1 | var createAction = require('redux-actions').createAction; 2 | 3 | var browser = require('../extension/browser'); 4 | 5 | module.exports.authenticate = function(request) { 6 | return function() { 7 | return browser.runtime.sendMessage(createAction('authenticate')(request)); 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /utils/entity-label.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | module.exports = function(entity, general) { 4 | return _.chain([entity.api, entity.type]) 5 | .compact() 6 | .union(entity.as && ['as', entity.as]) 7 | .union(general ? [] : ((entity.for && ['for', module.exports(entity.for)]) || [entity.id])) 8 | .join(' ') 9 | .value(); 10 | }; 11 | -------------------------------------------------------------------------------- /integrations/twitter/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "authenticatable": true, 3 | "account": { 4 | "stats": ["content", "followers", "following"] 5 | }, 6 | "content": { 7 | "stats": ["resposts", "likes"] 8 | }, 9 | "discussion": { 10 | "comments": { 11 | "stats": ["likes", "reposts"] 12 | }, 13 | "integrations": ["reddit"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /components/flex.styles.css: -------------------------------------------------------------------------------- 1 | .flex-row, 2 | .flex-center, 3 | .flex-spread { 4 | display: flex; 5 | align-items: center; 6 | } 7 | 8 | .flex-row { 9 | justify-content: center; 10 | } 11 | 12 | .flex-center { 13 | justify-content: left; 14 | } 15 | 16 | .flex-spread { 17 | justify-content: space-between; 18 | width: 100%; 19 | } 20 | 21 | .flex-grow { 22 | flex-grow: 1; 23 | } 24 | -------------------------------------------------------------------------------- /integrations/reddit/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": "client", 3 | "account": { 4 | "noImage": true, 5 | "stats": ["link_karma", "comment_karma", "date"] 6 | }, 7 | "content": { 8 | "stats": ["score", "score_ratio", "comments"] 9 | }, 10 | "discussion": { 11 | "comments": { 12 | "stats": ["score"] 13 | }, 14 | "integrations": ["reddit"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /redux/createStore.background.js: -------------------------------------------------------------------------------- 1 | var actions = require('./actions.background'); 2 | var createStore = require('./createStore.common'); 3 | 4 | module.exports = function(initialState) { 5 | return createStore( 6 | actions, 7 | { 8 | authentication: require('./authentication.reducer'), 9 | entities: require('./entities.reducer') 10 | }, 11 | initialState 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /integrations/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | counts: { 3 | grid: 21, 4 | listed: 30 5 | }, 6 | integrations: { 7 | imgur: require('./imgur/config'), 8 | instagram: require('./instagram/config'), 9 | reddit: require('./reddit/config'), 10 | soundcloud: require('./soundcloud/config'), 11 | twitter: require('./twitter/config'), 12 | youtube: require('./youtube/config') 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /redux/createStore.top-frame.js: -------------------------------------------------------------------------------- 1 | var actions = require('./actions.top-frame'); 2 | var createStore = require('./createStore.common'); 3 | 4 | module.exports = function(initialState) { 5 | return createStore( 6 | actions, 7 | { 8 | authentication: require('./authentication.reducer'), 9 | entities: require('./entities.reducer'), 10 | options: require('./options.reducer') 11 | }, 12 | initialState 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /integrations/imgur/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "content_security_policy": { 3 | "img-src": ["i.imgur.com"], 4 | "media-src": ["i.imgur.com"] 5 | }, 6 | "account": { 7 | "noImage": true, 8 | "stats": ["score", "date"] 9 | }, 10 | "content": { 11 | "stats": ["views", "score"] 12 | }, 13 | "discussion": { 14 | "comments": { 15 | "stats": ["score"] 16 | }, 17 | "integrations": ["reddit", "imgur"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /integrations/soundcloud/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": "client", 3 | "content_security_policy": { 4 | "img-src": ["i1.sndcdn.com"], 5 | "frame-src": ["w.soundcloud.com"] 6 | }, 7 | "account": { 8 | "stats": ["content", "followers", "following"] 9 | }, 10 | "content": { 11 | "stats": ["content", "likes", "views", "comments"] 12 | }, 13 | "discussion": { 14 | "integrations": ["reddit", "soundcloud"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | trim_trailing_whitespace = true 6 | 7 | [*.json] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_size = 2 11 | indent_style = space 12 | insert_final_newline = true 13 | 14 | [*.*{rc,config}] 15 | charset = utf-8 16 | end_of_line = lf 17 | indent_size = 2 18 | indent_style = space 19 | insert_final_newline = true 20 | 21 | [*.{css,md,yml}] 22 | indent_size = 2 23 | indent_style = space 24 | -------------------------------------------------------------------------------- /redux/options.actions.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | var createAction = require('redux-actions').createAction; 3 | 4 | var config = require('../extension/config'); 5 | 6 | var keys = config.options.keys(); 7 | 8 | module.exports.setOption = function(request) { 9 | return function(dispatch) { 10 | return (_.indexOf(keys, request.option, true) !== -1) && dispatch(createAction('SET_OPTION')(request)) && Promise.resolve(); 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /components/AccountHeader/AccountHeader.styles.css: -------------------------------------------------------------------------------- 1 | .banner { 2 | height: 126.66666667px; 3 | background-position: center; 4 | position: relative; 5 | background-size: cover; 6 | width: 100%; 7 | 8 | @raw { 9 | composes: flex-row from '../flex.styles'; 10 | } 11 | 12 | &:empty { 13 | display: block; 14 | } 15 | 16 | &-image { 17 | width: 33.33%; 18 | height: 126.66666667px; 19 | background-position: center; 20 | background-size: cover; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /components/Loading/Loading.styles.css: -------------------------------------------------------------------------------- 1 | /* TODO https://github.com/kogg/hovercards/issues/33 */ 2 | .loading-container { 3 | display: flex; 4 | align-items: center; 5 | justify-content: center; 6 | } 7 | 8 | .loading { 9 | background-image: url('../../assets/images/loading.gif'); 10 | background-position: center; 11 | background-repeat: no-repeat; 12 | background-size: 100%; 13 | display: block; 14 | height: 32px; 15 | margin: 16px auto; 16 | opacity: .7; 17 | width: 32px; 18 | } 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | All the text should be purple. **(The ask)** 2 | Everyone in the community demands it. **(The reasoning)** 3 | 4 | - [ ] All text in the extension is purple 5 | - [ ] All text in the website is purple 6 | - [ ] **(Tasks for completion)** 7 | 8 | This will require a change in all the css **(Implementation Details)** 9 | 10 | https://developer.mozilla.org/en-US/docs/Web/CSS/color_value 11 | http://www.w3schools.com/cssref/css_colors.asp 12 | **(Links to necessary documentation)** 13 | -------------------------------------------------------------------------------- /integrations/youtube/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "cache_length": 360000, 3 | "content_security_policy": { 4 | "img-src": ["yt3.ggpht.com", "*.googleusercontent.com"], 5 | "script-src": ["s.ytimg.com"] 6 | }, 7 | "account": { 8 | "stats": ["content", "followers", "views"] 9 | }, 10 | "content": { 11 | "stats": ["views", "likes", "dislikes"] 12 | }, 13 | "discussion": { 14 | "comments": { 15 | "stats": ["likes", "replies"] 16 | }, 17 | "integrations": ["reddit", "youtube"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /components/Loading/Loading.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var classnames = require('classnames'); 3 | 4 | var styles = require('./Loading.styles'); 5 | 6 | // TODO https://github.com/kogg/hovercards/issues/33 7 | module.exports = React.createClass({ 8 | displayName: 'Loading', 9 | propTypes: { 10 | className: React.PropTypes.string 11 | }, 12 | render: function() { 13 | return
; 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /extension/index.options.js: -------------------------------------------------------------------------------- 1 | require('../report'); 2 | require('string.prototype.endswith'); 3 | var Provider = require('react-redux').Provider; 4 | var React = require('react'); 5 | var ReactDOM = require('react-dom'); 6 | 7 | var Options = require('../components/Options/Options'); 8 | var createStore = require('../redux/createStore.options'); 9 | 10 | global.document.getElementById('mount').className = 'hovercards-root'; 11 | 12 | ReactDOM.render( 13 | 14 | 15 | , 16 | global.document.getElementById('mount') 17 | ); 18 | -------------------------------------------------------------------------------- /assets/images/fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /extension/index.top-frame.js: -------------------------------------------------------------------------------- 1 | require('../report'); 2 | require('string.prototype.endswith'); 3 | var Provider = require('react-redux').Provider; 4 | var React = require('react'); 5 | var ReactDOM = require('react-dom'); 6 | 7 | var Hovercards = require('../components/Hovercards/Hovercards'); 8 | var createStore = require('../redux/createStore.top-frame'); 9 | 10 | var element = document.documentElement.insertBefore(document.createElement('div'), null); 11 | element.className = 'hovercards-root'; 12 | 13 | ReactDOM.render( 14 | 15 | 16 | , 17 | element 18 | ); 19 | -------------------------------------------------------------------------------- /components/Image/Image.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var classnames = require('classnames'); 3 | 4 | var styles = require('./Image.styles'); 5 | 6 | module.exports = React.createClass({ 7 | displayName: 'Image', 8 | propTypes: { 9 | className: React.PropTypes.string, 10 | image: React.PropTypes.object.isRequired, 11 | onLoad: React.PropTypes.func.isRequired 12 | }, 13 | render: function() { 14 | return ( 15 | 17 | ); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /redux/analytics.actions.top-frame.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | var browser = require('../extension/browser'); 4 | 5 | module.exports.analytics = function(request) { 6 | return function() { 7 | var promise = browser.runtime.sendMessage({ type: 'analytics', payload: request }); 8 | 9 | if (!process.env.GOOGLE_ANALYTICS_ID) { 10 | promise = promise 11 | .then(function() { 12 | if (_.chain(request).first(2).isEqual(['send', 'exception']).value()) { 13 | console.error('google analytics', request); 14 | } else { 15 | console.debug('google analytics', request); 16 | } 17 | }); 18 | } 19 | return promise; 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /integrations/instagram/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | authenticatable: true, 3 | authentication_url: 'https://instagram.com/oauth/authorize/?scope=basic+public_content&client_id=' + process.env.INSTAGRAM_CLIENT_ID + '&redirect_uri=https://EXTENSION_ID.chromiumapp.org/callback&response_type=token', 4 | environment: 'client', 5 | content_security_policy: { 6 | 'img-src': ['scontent.cdninstagram.com'], 7 | 'media-src': ['scontent.cdninstagram.com'] 8 | }, 9 | account: { 10 | stats: ['content', 'followers', 'following'] 11 | }, 12 | content: { 13 | stats: ['likes', 'comments'] 14 | }, 15 | discussion: { 16 | integrations: ['instagram', 'reddit'] 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /components/Hovercard/Hovercard.styles.css: -------------------------------------------------------------------------------- 1 | .hovercard { 2 | align-items: flex-start; 3 | background: white; 4 | border-radius: 3px; 5 | box-shadow: rgba(0, 0, 0, 0.15) 0 2px 5px 0, rgba(0, 0, 0, 0.12) 0 2px 10px 0; 6 | box-sizing: border-box; 7 | justify-content: center; 8 | margin: 0; 9 | max-height: 600px; 10 | max-width: 620px; 11 | overflow: auto; 12 | position: absolute; 13 | z-index: 2147483647; 14 | 15 | &:empty { 16 | display: block; 17 | } 18 | } 19 | 20 | @raw { 21 | .lock-document::-webkit-scrollbar { 22 | display: none; 23 | } 24 | 25 | .lock-body { 26 | overflow: hidden; 27 | 28 | &::-webkit-scrollbar { 29 | display: none; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /www/css/privacy.css: -------------------------------------------------------------------------------- 1 | @import './index.css'; 2 | 3 | .sub-header { 4 | text-align: center; 5 | color: white; 6 | padding: 50px 20px; 7 | background: #b4e6f7; 8 | } 9 | 10 | .sub-header h1 { 11 | font-size: 60px; 12 | } 13 | 14 | .center-container { 15 | max-width: 780px; 16 | width: 100%; 17 | margin: auto; 18 | padding: 50px 20px; 19 | } 20 | 21 | .login-text a { 22 | text-decoration: none; 23 | color: white; 24 | } 25 | 26 | p { 27 | margin-bottom: 20px; 28 | } 29 | 30 | a { 31 | color: blue; 32 | } 33 | 34 | @media (max-width: 760px) { 35 | .sub-header h1 { 36 | font-size: 30px; 37 | } 38 | } 39 | 40 | @media (max-width: 460px) { 41 | .sub-header h1 { 42 | font-size: 25px; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /assets/images/twitter-retweet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ![Inject your contributions!](../assets/images/inject-the-binary.jpg) 2 | 3 | ## Issues 4 | We encourage you to create issues. We've put an [issue template](ISSUE_TEMPLATE.md) in place to get you started. We use [robinpowered's github labels](https://robinpowered.com/blog/best-practice-system-for-organizing-and-tagging-github-issues/) for our issues, loosely. 5 | 6 | ## [Development](../README.md#development) 7 | 8 | ## Join us! 9 | We just opened up HoverCards to the world, so we're looking for this to be a community driven project. Our documentation is very spotty and looking for love. Feel free to create issues, chat with us in [our gitter](https://gitter.im/kogg/hovercards), and throw us some pull requests of your own! 10 | 11 | ![Bye!](../assets/images/carlito-corner.png) 12 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hovercards", 3 | "description": "Get more content. Open fewer tabs.", 4 | "main": [ 5 | "dist/background.js", 6 | "dist/options.js", 7 | "dist/top-frame.js" 8 | ], 9 | "license": "MIT", 10 | "ignore": [ 11 | ".env", 12 | "coverage/*", 13 | "dist-*", 14 | "dist/*", 15 | "dump.rdb", 16 | "bower_components/*", 17 | "node_modules/*", 18 | "npm-debug.log*" 19 | ], 20 | "authors": [ 21 | "Cameron Rohani (https://twitter.com/cameronrohani)", 22 | "Marco Marandiz (http://marcomarandiz.com/)", 23 | "Saiichi Hashimoto (http://saiichihashimoto.com/)" 24 | ], 25 | "homepage": "http://hovercards.com", 26 | "repository": { 27 | "type": "git", 28 | "url": "git://github.com/kogg/hovercards.git" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /assets/images/close-button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /components/Media/Media.styles.css: -------------------------------------------------------------------------------- 1 | .media { 2 | height: inherit; 3 | max-width: 100%; 4 | min-height: inherit; 5 | min-width: inherit; 6 | padding: 18px 24px 0 24px; 7 | position: relative; 8 | text-align: left; 9 | width: 9999px; 10 | 11 | @raw { 12 | composes: flex-row from '../flex.styles'; 13 | } 14 | 15 | &:empty { 16 | display: none; 17 | } 18 | } 19 | 20 | .media + .meta { 21 | display: none; 22 | } 23 | 24 | .description { 25 | padding: 12px 0; 26 | text-align: left; 27 | width: 100%; 28 | } 29 | 30 | .text, 31 | .name { 32 | color: rgba(0, 0, 0, .8); 33 | font-size: 14px; 34 | } 35 | 36 | .text { 37 | color: rgba(0, 0, 0, .6); 38 | } 39 | 40 | .name { 41 | font-weight: bold; 42 | margin-right: 6px; 43 | } 44 | 45 | .name:empty { 46 | display: none; 47 | } 48 | -------------------------------------------------------------------------------- /utils/format.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | var browser = require('../extension/browser'); 4 | 5 | var units = ['', 'x_thousand', 'x_million', 'x_billion', 'x_trillion']; 6 | 7 | module.exports.number = function(number) { 8 | if (!_.isNumber(number) || _.isNaN(number)) { 9 | return number; 10 | } 11 | if (number < 10000) { 12 | return number.toLocaleString(); 13 | } 14 | var log1000 = Math.floor(Math.log10(number) / 3); 15 | var multiple1000 = number / Math.pow(1000, log1000); 16 | var roundto = Math.pow(10, 2 - Math.floor(Math.log10(multiple1000))); 17 | multiple1000 = Math.round(multiple1000 * roundto) / roundto; 18 | 19 | if (!log1000) { 20 | return multiple1000.toLocaleString(); 21 | } 22 | return browser.i18n.getMessage(units[log1000], [multiple1000.toLocaleString()]); 23 | }; 24 | -------------------------------------------------------------------------------- /components/Discussions/Discussions.styles.css: -------------------------------------------------------------------------------- 1 | .discussions-container { 2 | height: inherit; 3 | max-width: 100%; 4 | overflow: hidden; 5 | text-align: left; 6 | width: 9999px; 7 | z-index: 999; 8 | } 9 | 10 | .discussions { 11 | overflow: hidden; 12 | height: calc(100% - 49px); 13 | } 14 | 15 | .tabs-container { 16 | background: #f0f0f0; 17 | padding: 12px 24px; 18 | 19 | @raw { 20 | composes: flex-spread from '../flex.styles'; 21 | } 22 | } 23 | 24 | .tabs { 25 | display: static; 26 | } 27 | 28 | .tab { 29 | color: rgba(0, 0, 0, .66); 30 | cursor: pointer; 31 | display: inline-block; 32 | font-weight: 400; 33 | margin: 0 4px; 34 | font-size: 15px; 35 | 36 | &:first-of-type { 37 | margin-left: 0; 38 | } 39 | 40 | &.selected { 41 | color: rgba(0, 0, 0, .8); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "rules": { 4 | "at-rule-name-space-after": "always", 5 | "block-opening-brace-space-before": ["always", { "ignoreAtRules": "raw" }], 6 | "color-hex-length": null, 7 | "comment-word-blacklist": [['TODO', 'FIXME', 'HACK'], { "severity": "warning" }], 8 | "declaration-block-no-duplicate-properties": [true, { "ignoreProperties": ["composes"] }], 9 | "declaration-block-no-ignored-properties": [true, { "severity": "warning" }], 10 | "number-leading-zero": null, 11 | "property-no-unknown": [true, { "severity": "warning", "ignoreProperties": ["composes", "font-smooth", "text-size-adjust"] }], 12 | "selector-pseudo-element-colon-notation": "single", 13 | "shorthand-property-no-redundant-values": [true, { "severity": "warning" }], 14 | "string-quotes": "single" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /redux/entities.reducer.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | var handleActions = require('redux-actions').handleActions; 3 | 4 | var entityLabel = require('../utils/entity-label'); 5 | 6 | module.exports = handleActions( 7 | { 8 | CLEAR_ENTITIES: { 9 | next: function(state, action) { 10 | return _.omit(state, function(value, key) { 11 | return key.startsWith(action.payload); 12 | }); 13 | } 14 | }, 15 | SET_ENTITY: { 16 | next: function(state, action) { 17 | return Object.assign({}, state, { [(action.meta || {}).label || entityLabel(action.payload)]: action.payload }); 18 | }, 19 | throw: function(state, action) { 20 | return Object.assign({}, state, { 21 | [entityLabel(action.payload.request)]: Object.assign({}, state[entityLabel(action.payload.request)], { err: action.payload }) 22 | }); 23 | } 24 | } 25 | }, 26 | {} 27 | ); 28 | -------------------------------------------------------------------------------- /components/meta.styles.css: -------------------------------------------------------------------------------- 1 | .meta { 2 | margin-top: 12px; 3 | padding: 12px 24px; 4 | border-top: 1px solid rgba(0, 0, 0, .1); 5 | 6 | @raw { 7 | composes: flex-row from './flex.styles'; 8 | composes: flex-spread from './flex.styles'; 9 | } 10 | } 11 | 12 | .mediameta .meta { 13 | border-top: none; 14 | margin-top: 4px; 15 | } 16 | 17 | .meta-main-container { 18 | position: relative; 19 | width: 100%; 20 | height: 20px; 21 | } 22 | 23 | .meta-main { 24 | transition: all .3s ease-in-out; 25 | 26 | .meta-item { 27 | margin: 0 8px 0 0; 28 | } 29 | } 30 | 31 | .meta-item span, 32 | .meta-item { 33 | line-height: 20px; 34 | font-size: 14px; 35 | color: rgba(0, 0, 0, .6); 36 | } 37 | 38 | .meta-number { 39 | font-size: 14px; 40 | font-weight: 500; 41 | color: rgba(0, 0, 0, .8); 42 | 43 | span { 44 | font-weight: 500; 45 | color: rgba(0, 0, 0, .8); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /report/index.browser.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | var Raven = require('raven-js'); 3 | 4 | var browser = require('../extension/browser'); 5 | 6 | Raven 7 | .config(process.env.SENTRY_DSN_CLIENT, { 8 | environment: process.env.NODE_ENV, 9 | release: process.env.npm_package_version 10 | }) 11 | .install(); 12 | 13 | browser.storage.onChanged.addListener(function(changes, areaName) { 14 | if (areaName !== 'sync') { 15 | return; 16 | } 17 | _.pairs(changes).forEach(function(entry) { 18 | if (entry[0] !== 'user_id') { 19 | return; 20 | } 21 | Raven.setUserContext({ id: entry[1].newValue }); 22 | }); 23 | }); 24 | 25 | browser.storage.sync.get('user_id') 26 | .then(_.property('user_id')) 27 | .then(function(user_id) { 28 | if (!user_id) { 29 | return; 30 | } 31 | 32 | Raven.setUserContext({ id: user_id }); 33 | }) 34 | .catch(Raven.captureException); 35 | 36 | module.exports = Raven; 37 | -------------------------------------------------------------------------------- /components/Gif/Gif.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var classnames = require('classnames'); 3 | 4 | var styles = require('./Gif.styles'); 5 | 6 | module.exports = React.createClass({ 7 | displayName: 'Gif', 8 | propTypes: { 9 | className: React.PropTypes.string, 10 | gif: React.PropTypes.string.isRequired, 11 | image: React.PropTypes.object, 12 | onLoad: React.PropTypes.func.isRequired 13 | }, 14 | render: function() { 15 | return ( 16 |