Skip to content

Instantly share code, notes, and snippets.

@justbc
Created January 31, 2014 20:32
Show Gist options
  • Save justbc/8742548 to your computer and use it in GitHub Desktop.
Save justbc/8742548 to your computer and use it in GitHub Desktop.
A Pen by Brian C.
<!--
Join over 70,000 developers in our beta program at http://famo.us.
Famo.us is a free opensource Javascript development platform that dan build apps, games and interfaces using HTML5 and WebGL.
What makes us unique is that we have built a pure JS renderer and 3D physics engine and we've wrapped those core engines with an enterprise grade platform that's easy to use.
Each week we release new examples. If you are a blogger and would like early access so you can write about them conact [email protected].
We are expecting to be in full public beta late February of 2014.
-->
//CodePen Evaluation License
//
//Copyright (c) 2013 Famo.us, Inc.
//
// Non-sublicensable permission is hereby granted, free of charge,
// to any person obtaining a copy of this software and associated
// documentation files directly from codepen.io (the "Software"), solely to
// internally make and internally use copies of the Software to test,
// explore, and evaluate the Software solely in such person’s non-commercial,
// non-production environments, provided that the above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Famous(function(require) {
// import dependencies
var FamousEngine = require('famous/Engine');
var FastClick = require('famous-sync/FastClick');
var Scrollview = require('famous-views/Scrollview');
var ViewSequence = require('famous/ViewSequence');
var Surface = require('famous/Surface');
// boilerplate: the application template (normally in a separate require)
var View = require('famous/View');
var EventHandler = require('famous/EventHandler');
var OptionsManager = require('famous/OptionsManager');
var RenderNode = require('famous/RenderNode');
var Utility = require('famous/Utility');
var HeaderFooterLayout = require('famous-views/HeaderFooterLayout');
var EdgeSwapper = require('famous-views/EdgeSwapper');
var NavigationBar = require('famous-widgets/NavigationBar');
var TitleBar = require('famous-widgets/TitleBar');
function App(options) {
// extend from view
View.apply(this, arguments);
// create the layout
this.layout = new HeaderFooterLayout();
// create the header
this.header = new TitleBar(this.options.header);
// create the navigation bar
this.navigation = new NavigationBar(this.options.navigation);
// create the content area
this.contentArea = new EdgeSwapper(this.options.content);
// link endpoints of layout to widgets
this.layout.id['header'].link(this.header);
this.layout.id['footer'].link(Utility.transformInFront).link(this.navigation);
this.layout.id['content'].link(Utility.transformBehind).link(this.contentArea);
// assign received events to content area
this.eventInput.pipe(this.contentArea);
// navigation events are app events
EventHandler.setOutputHandler(this, this.navigation);
// declare the render nodes
this._currentSection = undefined;
this._sections = {};
this._sectionTitles = {};
// respond to the the selection of a different section
this.navigation.on('select', function(data) {
this._currentSection = data.id;
this.header.show(this._sectionTitles[data.id]);
this.contentArea.show(this._sections[data.id].get());
}.bind(this));
// assign the layout to this view
this._link(this.layout);
};
App.prototype = Object.create(View.prototype);
App.prototype.constructor = App;
App.DEFAULT_OPTIONS = {
header: {
size: [undefined, 50],
inTransition: true,
outTransition: true,
look: {
size: [undefined, 50]
}
},
navigation: {
size: [undefined, 50],
direction: Utility.Direction.X,
buttons: {
inTransition: true,
outTransition: true
}
},
content: {
inTransition: true,
outTransition: true,
overlap: true
}
};
App.prototype.getState = function() {
return this._currentSection;
};
App.prototype.section = function(id) {
// create the section if it doesn't exist
if(!(id in this._sections)) {
this._sections[id] = new RenderNode();
// make it possible to set the section's properties
this._sections[id].setOptions = (function(options) {
this._sectionTitles[id] = options.title;
this.navigation.defineSection(id, {
content: '<span class="icon">' + options.navigation.icon + '</span><br />' + options.navigation.caption
});
}).bind(this);
}
return this._sections[id];
};
App.prototype.select = function(id) {
this._currentSection = id;
if(!(id in this._sections)) return false;
this.navigation.select(id);
return true;
};
// boilerplate: end
var FeedItem = require('famous-widgets/FeedItem');
var Transitionable = require('famous/Transitionable');
var OptionsManager = require('famous/OptionsManager');
var Matrix = require('famous/Matrix');
var userInfoFirebase = null;
var userInfo = {};
function SparkItem(options) {
FeedItem.apply(this, arguments);
if(options) this.setOptions(options);
this.inState = new Transitionable(0);
this.inState.set(1, this.options.inTransition);
};
SparkItem.prototype = Object.create(FeedItem.prototype);
SparkItem.prototype.constructor = SparkItem;
SparkItem.DEFAULT_OPTIONS = OptionsManager.patch(FeedItem.DEFAULT_OPTIONS, {
classes: ['tweet'],
size: [undefined, 80],
inTransition: {curve: 'easeOut', duration: 500}
});
SparkItem.setFirebase = function(firebase) {
userInfoFirebase = firebase;
};
SparkItem.prototype.render = function(input) {
var result = FeedItem.prototype.render.apply(this, arguments);
if(!this.inState.isActive()) return result;
else return {opacity: this.inState.get(), target: result};
};
SparkItem.prototype.setContent = function(content) {
var userId = content['author'];
var iconUrl = 'content/default-pic.gif';
if(userId in userInfo) {
iconUrl = userInfo[userId]['pic'];
}
else {
var userRef = userInfoFirebase.child('people').child(userId);
userRef.on('value', function(dataSnapshot) {
userInfo[userId] = dataSnapshot.val();
if(userInfo[userId]['pic']) this.setContent(content);
}.bind(this));
}
var feedContent = {icon: iconUrl, source: content['by'], time: content['timestamp'], text: content['content']};
return FeedItem.prototype.setContent.call(this, feedContent);
};
// define the options
var headerOptions = {
look: {classes: ['header']},
inTransition: {curve: 'easeOutBounce', duration: 375},
outTransition: {curve: 'easeIn', duration: 225},
overlap: false
};
var navigationOptions = {
buttons: {
onClasses: ['navigation', 'on'],
offClasses: ['navigation', 'off'],
inTransition: {curve: 'easeInOut', duration: 150},
outTransition: {curve: 'easeInOut', duration: 150}
}
};
var contentOptions = {
inTransition: {curve: 'easeOutBounce', duration: 500},
outTransition: {duration: 300},
overlap: true
};
// create the App from the template and hook it into the context
var myApp = new App({header: headerOptions, navigation: navigationOptions, content: contentOptions});
var mainDisplay = FamousEngine.createContext();
mainDisplay.link(myApp);
FamousEngine.pipe(myApp);
// setup Firebase
var firebase = new Firebase('https://firefeed.firebaseio.com/');
SparkItem.setFirebase(firebase);
// create the 'Home' section
var homeSection = myApp.section('home');
homeSection.setOptions({
title: '<span class="bird">&#62217;</span>',
navigation: {caption: 'Home', icon: '<span class="entypo">&#8962;</span>'}
});
var homeItems = new ViewSequence();
var homeScroll = new Scrollview();
homeSection.link(homeScroll);
// create the 'Connect' section
var connectSection = myApp.section('connect');
connectSection.setOptions({
title: 'Connect',
navigation: {caption: 'Connect', icon: '@'}
});
var connectItems = new ViewSequence();
var connectScroll = new Scrollview();
connectSection.link(connectScroll);
// create the 'Discover' section
var discoverSection = myApp.section('discover');
discoverSection.setOptions({
title: 'Discover',
navigation: {caption: 'Discover', icon: '#'}
});
var discoverItems = new ViewSequence();
var discoverScroll = new Scrollview();
discoverSection.link(discoverScroll);
// create the 'Me' section
var meSection = myApp.section('me');
meSection.setOptions({
title: 'Me',
navigation: {caption: 'Me', icon: '<span class="entypo">&#128100;</span>'}
});
// stubbed default to Mark for demo purposes
var myAuth = {
'id': '819290432',
'pic': 'https://graph.facebook.com/819290432/picture/?width=200&height=200&return_ssl_resources=1',
'fullName': 'Mark Lu',
'location': 'San Francisco'
};
var profile = new Surface({
size: [undefined, 160],
classes: ['profile'],
content: '<img src="' + myAuth['pic'] + '" /><p>' + myAuth['fullName'] + ' &bull; ' + myAuth['location'] + '</p>'
});
var meItems = new ViewSequence([profile]); // start with profile
var meScroll = new Scrollview();
meSection.link(meScroll);
// populate the scrollviews
var sparks = firebase.child('sparks');
// display the scrollviews when loaded
sparks.once('value', function() {
// rewind the pointers
while(homeItems.getPrevious()) homeItems = homeItems.getPrevious();
while(discoverItems.getPrevious()) discoverItems = discoverItems.getPrevious();
while(connectItems.getPrevious()) connectItems = connectItems.getPrevious();
while(meItems.getPrevious()) meItems = meItems.getPrevious();
// hook them up
homeScroll.sequenceFrom(homeItems);
discoverScroll.sequenceFrom(discoverItems);
connectScroll.sequenceFrom(connectItems);
meScroll.sequenceFrom(meItems);
});
// update the scrollviews with data as they come in
var recentTimestampLimit = Date.now() - 90*86400000; // limit to 90 days
sparks.on('child_added', function(snapshot) {
var value = snapshot.val();
if(value['timestamp'] > recentTimestampLimit) {
homeItems.unshift(new SparkItem({content: value}));
homeScroll.goToPreviousPage();
}
if(value['author'] === myAuth['id']) {
meItems.splice(1, 0, new SparkItem({content: value}));
meScroll.goToPreviousPage();
}
if(value['content'].indexOf('@') >= 0) {
connectItems.unshift(new SparkItem({content: value}));
connectScroll.goToPreviousPage();
}
if(value['content'].indexOf('#') >= 0) {
discoverItems.unshift(new SparkItem({content: value}));
discoverScroll.goToPreviousPage();
}
});
// start on the home section
myApp.select('home');
// signup button
var Modifier = require('famous/Modifier');
var Matrix = require('famous/Matrix');
var signupContext = FamousEngine.createContext();
signupContext.link(new Modifier({origin: [0, 0], transform: Matrix.translate(10, 10), opacity: 0.7})).link(new Surface({
size: [true, true],
classes: ['signup'],
content: '<a href="http://demo.famo.us/signup/">sign up</a>'
}));
});
body {
font-family: open_sansregular, sans-serif;
background-image: url("/tweetus/content/mback_small.png");
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
vertical-align: middle;
text-align: center;
}
.header {
background-color: #4294ce;
color: #fff;
background: #76c5fb; /* Old browsers */
background: -moz-linear-gradient(top, #76c5fb 0%, #60abe2 1%, #2978b1 99%, #18659d 100%); /* FF3.6+ */
background: -webkit-linear-gradient(top, #76c5fb 0%,#60abe2 1%,#2978b1 99%,#18659d 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #76c5fb 0%,#60abe2 1%,#2978b1 99%,#18659d 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #76c5fb 0%,#60abe2 1%,#2978b1 99%,#18659d 100%); /* IE10+ */
background: linear-gradient(to bottom, #76c5fb 0%,#60abe2 1%,#2978b1 99%,#18659d 100%); /* W3C */
font-family: open_sansbold, sans-serif;
font-size: 18px;
line-height: 26px;
padding: 10px;
text-align: center;
text-shadow: rgba(0,0,0,0.75) 0px -1px 0px;
z-index: 1;
}
.header .bird {
font-family: entypo-social;
font-size: 60px;
color: hsl(207,75%,70%);
}
.header .icon {
display: block;
height: 100%;
top: 0px;
right: 0px;
letter-spacing: 10px;
position: absolute;
font-size: 46px;
line-height: 46px;
font-family: 'entypo';
}
.navigation {
padding: 3px;
font-family: open_sansbold;
font-size: 10px;
line-height: 10px;
text-align: center;
vertical-align: middle;
text-shadow: rgba(0,0,0,0.75) 0px -1px 0px;
z-index: 1;
}
.navigation.off {
color: #999;
background: #000000; /* Old browsers */
background: -moz-linear-gradient(top, #000000 0%, #595959 1%, #383838 4%, #101010 98%, #000000 100%); /* FF3.6+ */
background: -webkit-linear-gradient(top, #000000 0%,#595959 1%,#383838 4%,#101010 98%,#000000 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #000000 0%,#595959 1%,#383838 4%,#101010 98%,#000000 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #000000 0%,#595959 1%,#383838 4%,#101010 98%,#000000 100%); /* IE10+ */
background: linear-gradient(to bottom, #000000 0%,#595959 1%,#383838 4%,#101010 98%,#000000 100%); /* W3C */
}
.navigation.on {
color: #fff;
background: #212121; /* Old browsers */
background: -moz-linear-gradient(top, #212121 0%, #0f0f0f 100%); /* FF3.6+ */
background: -webkit-linear-gradient(top, #212121 0%,#0f0f0f 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #212121 0%,#0f0f0f 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #212121 0%,#0f0f0f 100%); /* IE10+ */
background: linear-gradient(to bottom, #212121 0%,#0f0f0f 100%); /* W3C */
box-shadow: #000 0px 0px 3px 2px inset;
}
.navigation .icon {
display: inline-block;
font-size: 26px;
line-height: 30px;
}
.navigation .icon .entypo {
font-family: entypo;
font-size: 48px;
line-height: 26px;
vertical-align: bottom;
}
.navigation.on .icon {
color: hsl(207, 75%, 65%);
}
.tweet {
background-color: #ffffff;
padding: 10px;
border-top: 1px solid #eee;
font-size: 12px;
text-align: left;
}
p {
padding: 0px;
margin: 0px;
border: 0px;
}
.tweet .icon {
float: left;
margin-right: 10px;
}
.tweet .source {
color: #000000;
font-family: open_sansbold;
text-align: left;
}
.tweet .source .screenname {
color: #909090;
font-family: open_sansregular;
}
.tweet .text {
color: #000000;
text-align: left;
text-overflow:ellipsis;
overflow: hidden;
}
.tweet .time {
color: #909090;
float: right;
}
.profile {
color: #fff;
background: #686868; /* Old browsers */
background: -moz-radial-gradient(center, ellipse cover, #686868 0%, #141414 100%); /* FF3.6+ */
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,#686868), color-stop(100%,#141414)); /* Chrome,Safari4+ */
background: -webkit-radial-gradient(center, ellipse cover, #686868 0%,#141414 100%); /* Chrome10+,Safari5.1+ */
background: -o-radial-gradient(center, ellipse cover, #686868 0%,#141414 100%); /* Opera 12+ */
background: -ms-radial-gradient(center, ellipse cover, #686868 0%,#141414 100%); /* IE10+ */
background: radial-gradient(ellipse at center, #686868 0%,#141414 100%); /* W3C */
font-family: open_sansregular, sans-serif;
font-size: 18px;
line-height: 26px;
padding: 20px;
text-align: center;
text-shadow: rgba(0,0,0,0.75) 0px -1px 0px;
}
.profile img {
border: 5px solid #fff;
width: 80px;
height: 80px;
border-radius: 5px;
}

Twitter Example With Firebase

A famo.us example app. Showcases our javascript powered infinite scrollview, and multiple pages. All data is sent over firebase.

A Pen by Brian C on CodePen.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment