├── README.md ├── .gitignore ├── gulp ├── tasks │ ├── default.js │ ├── build.js │ ├── setWatch.js │ ├── markup.js │ ├── browserSync.js │ ├── images.js │ ├── watch.js │ ├── sass.js │ └── browserify.js ├── index.js └── util │ ├── handleErrors.js │ └── bundleLogger.js ├── Gemfile ├── src ├── images │ └── gulp.png ├── sass │ ├── _scss-mixins.scss │ ├── _typography.sass │ ├── app.sass │ ├── app.css.scss │ └── _famous.sass ├── javascript │ ├── models │ │ └── card.coffee │ ├── templates │ │ └── menu.hbs │ ├── app.coffee │ ├── views │ │ ├── menu │ │ │ ├── menu-button.coffee │ │ │ └── menu-interior.coffee │ │ ├── cards │ │ │ ├── card-stack.coffee │ │ │ ├── card-interior.coffee │ │ │ └── card.coffee │ │ ├── components │ │ │ └── slideout.coffee │ │ └── main-view.coffee │ └── collections │ │ └── cards.coffee └── htdocs │ └── index.html ├── config.rb ├── gulpfile.js ├── Gemfile.lock ├── LICENSE.md └── package.json /README.md: -------------------------------------------------------------------------------- 1 | Browser imgur using Famo.us. 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .sass-cache 3 | build 4 | Desktop.ini 5 | node_modules 6 | API_KEY.coffee -------------------------------------------------------------------------------- /gulp/tasks/default.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | gulp.task('default', ['watch']); 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'sass', '>= 3.3' 4 | gem 'compass', '>= 1.0.0.rc.1' -------------------------------------------------------------------------------- /src/images/gulp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenGrider/Famous-SlideTemplateApp/master/src/images/gulp.png -------------------------------------------------------------------------------- /gulp/tasks/build.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | gulp.task('build', ['browserify', 'sass', 'images', 'markup']); 4 | -------------------------------------------------------------------------------- /gulp/tasks/setWatch.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | gulp.task('setWatch', function() { 4 | global.isWatching = true; 5 | }); 6 | -------------------------------------------------------------------------------- /src/sass/_scss-mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin font-smoothing { 2 | -moz-osx-font-smoothing: grayscale; 3 | -webkit-font-smoothing: antialiased; 4 | } 5 | -------------------------------------------------------------------------------- /src/sass/_typography.sass: -------------------------------------------------------------------------------- 1 | body 2 | +font-smoothing // <- _mixins.scss 3 | +translateZ(0) // <- Compass 4 | color: #555 5 | font-family: sans-serif -------------------------------------------------------------------------------- /gulp/index.js: -------------------------------------------------------------------------------- 1 | var requireDir = require('require-dir'); 2 | 3 | // Require all tasks in gulp/tasks, including subfolders 4 | requireDir('./tasks', { recurse: true }); 5 | -------------------------------------------------------------------------------- /gulp/tasks/markup.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | gulp.task('markup', function() { 4 | return gulp.src('src/htdocs/**') 5 | .pipe(gulp.dest('build')); 6 | }); 7 | -------------------------------------------------------------------------------- /src/javascript/models/card.coffee: -------------------------------------------------------------------------------- 1 | Backbone = require('Backbone') 2 | 3 | class Card extends Backbone.Model 4 | getImageSrc: -> 5 | @get('link').replace('.jpg', 'l.jpg').replace('.png', 'l.png') 6 | 7 | 8 | module.exports = Card 9 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | # Compass Config 2 | preferred_syntax = :sass 3 | http_path = '/' 4 | css_dir = 'build' 5 | sass_dir = 'src/sass' 6 | images_dir = 'build/images' 7 | fonts_dir = 'build/fonts' 8 | relative_assets = true 9 | line_comments = true 10 | -------------------------------------------------------------------------------- /gulp/tasks/browserSync.js: -------------------------------------------------------------------------------- 1 | var browserSync = require('browser-sync'); 2 | var gulp = require('gulp'); 3 | 4 | gulp.task('browserSync', ['build'], function() { 5 | browserSync.init(['build/**'], { 6 | server: { 7 | baseDir: ['build', 'src'] 8 | } 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/javascript/templates/menu.hbs: -------------------------------------------------------------------------------- 1 |

Filters

2 | 3 | 4 | 10 | -------------------------------------------------------------------------------- /src/javascript/app.coffee: -------------------------------------------------------------------------------- 1 | $ = require('jquery') 2 | Backbone = require('backbone') 3 | Backbone.$ = $ 4 | CardsCollection = require('./collections/cards') 5 | MainView = require('./views/main-view') 6 | 7 | 8 | context = require('famous/core/engine').createContext() 9 | collection = new CardsCollection 10 | mainView = new MainView(collection: collection) 11 | context.add(mainView) 12 | -------------------------------------------------------------------------------- /src/javascript/views/menu/menu-button.coffee: -------------------------------------------------------------------------------- 1 | _ = require('underscore') 2 | 3 | class MenuButton extends require('famous/core/surface') 4 | constructor: (options) -> 5 | super 6 | @value = options.value 7 | @on('click', @onClick) 8 | 9 | spin: => 10 | 11 | onClick: => 12 | @emit('button:clicked', @value, this) 13 | 14 | 15 | module.exports = MenuButton 16 | -------------------------------------------------------------------------------- /gulp/tasks/images.js: -------------------------------------------------------------------------------- 1 | var changed = require('gulp-changed'); 2 | var gulp = require('gulp'); 3 | var imagemin = require('gulp-imagemin'); 4 | 5 | gulp.task('images', function() { 6 | var dest = './build/images'; 7 | 8 | return gulp.src('./src/images/**') 9 | .pipe(changed(dest)) // Ignore unchanged files 10 | .pipe(imagemin()) // Optimize 11 | .pipe(gulp.dest(dest)); 12 | }); 13 | -------------------------------------------------------------------------------- /gulp/util/handleErrors.js: -------------------------------------------------------------------------------- 1 | var notify = require("gulp-notify"); 2 | 3 | module.exports = function() { 4 | 5 | var args = Array.prototype.slice.call(arguments); 6 | 7 | // Send error to notification center with gulp-notify 8 | notify.onError({ 9 | title: "Compile Error", 10 | message: "<%= error.message %>" 11 | }).apply(this, args); 12 | 13 | // Keep gulp from hanging on this task 14 | this.emit('end'); 15 | }; -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | gulpfile.js 3 | =========== 4 | Rather than manage one giant configuration file responsible 5 | for creating multiple tasks, each task has been broken out into 6 | its own file in gulp/tasks. Any file in that folder gets automatically 7 | required by the loop in ./gulp/index.js (required below). 8 | 9 | To add a new task, simply add a new task file to gulp/tasks. 10 | */ 11 | 12 | require('./gulp'); -------------------------------------------------------------------------------- /gulp/tasks/watch.js: -------------------------------------------------------------------------------- 1 | /* Notes: 2 | - gulp/tasks/browserify.js handles js recompiling with watchify 3 | - gulp/tasks/browserSync.js automatically reloads any files 4 | that change within the directory it's serving from 5 | */ 6 | 7 | var gulp = require('gulp'); 8 | 9 | gulp.task('watch', ['setWatch', 'browserSync'], function() { 10 | gulp.watch('src/sass/**', ['sass']); 11 | gulp.watch('src/images/**', ['images']); 12 | gulp.watch('src/htdocs/**', ['markup']); 13 | }); 14 | -------------------------------------------------------------------------------- /gulp/tasks/sass.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var sass = require('gulp-ruby-sass'); 3 | var handleErrors = require('../util/handleErrors'); 4 | var browserSync = require('browser-sync'); 5 | 6 | gulp.task('sass', ['images'], function () { 7 | return gulp.src('src/sass/*.{sass, scss}') 8 | .pipe(sass({ 9 | compass: true, 10 | bundleExec: true, 11 | sourcemap: true, 12 | sourcemapPath: '../sass' 13 | })) 14 | .on('error', handleErrors) 15 | .pipe(gulp.dest('build')); 16 | }); 17 | -------------------------------------------------------------------------------- /gulp/util/bundleLogger.js: -------------------------------------------------------------------------------- 1 | /* bundleLogger 2 | ------------ 3 | Provides gulp style logs to the bundle method in browserify.js 4 | */ 5 | 6 | var gutil = require('gulp-util'); 7 | var prettyHrtime = require('pretty-hrtime'); 8 | var startTime; 9 | 10 | module.exports = { 11 | start: function() { 12 | startTime = process.hrtime(); 13 | gutil.log('Running', gutil.colors.green("'bundle'") + '...'); 14 | }, 15 | 16 | end: function() { 17 | var taskTime = process.hrtime(startTime); 18 | var prettyTime = prettyHrtime(taskTime); 19 | gutil.log('Finished', gutil.colors.green("'bundle'"), 'in', gutil.colors.magenta(prettyTime)); 20 | } 21 | }; -------------------------------------------------------------------------------- /src/htdocs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Swipey App 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | chunky_png (1.3.1) 5 | compass (1.0.0.rc.1) 6 | chunky_png (~> 1.2) 7 | compass-core (~> 1.0.0.rc.1) 8 | compass-import-once (~> 1.0.5) 9 | rb-fsevent (>= 0.9.3) 10 | rb-inotify (>= 0.9) 11 | sass (>= 3.3.13, < 3.5) 12 | compass-core (1.0.0.rc.1) 13 | multi_json (~> 1.0) 14 | sass (>= 3.3.0, < 3.5) 15 | compass-import-once (1.0.5) 16 | sass (>= 3.2, < 3.5) 17 | ffi (1.9.3) 18 | multi_json (1.10.1) 19 | rb-fsevent (0.9.4) 20 | rb-inotify (0.9.5) 21 | ffi (>= 0.5.0) 22 | sass (3.3.14) 23 | 24 | PLATFORMS 25 | ruby 26 | 27 | DEPENDENCIES 28 | compass (>= 1.0.0.rc.1) 29 | sass (>= 3.3) 30 | -------------------------------------------------------------------------------- /src/sass/app.sass: -------------------------------------------------------------------------------- 1 | @import compass 2 | @import scss-mixins 3 | @import typography 4 | @import famous 5 | 6 | 7 | .card 8 | border-radius: 8px 9 | box-shadow: 0 0 6px 8px rgba(0, 0, 0, 0.5) 10 | 11 | .card-description 12 | text-align: center 13 | font: 16px 14 | box-shadow: 0 0 6px 8px rgba(0, 0, 0, 0.5) 15 | background-color: rgba(0, 0, 0, .3) 16 | color: white 17 | 18 | .background-image 19 | -webkit-filter: blur(7px) 20 | z-index: -999 21 | 22 | body 23 | background-color: black 24 | 25 | .menu-bar 26 | background-color: blue 27 | z-index: 10 28 | 29 | .menu-button 30 | text-align: center 31 | font: 16px 32 | box-shadow: 0 0 6px 8px rgba(0, 0, 0, 0.5) 33 | background-color: rgba(150, 150, 150, .4) 34 | color: black 35 | line-height: 140px 36 | 37 | .selected 38 | background-color: rgba(250, 250, 250, .8) 39 | 40 | .settings 41 | color: white 42 | text-align: center 43 | -------------------------------------------------------------------------------- /src/javascript/views/cards/card-stack.coffee: -------------------------------------------------------------------------------- 1 | RenderController = require('famous/views/rendercontroller') 2 | Card = require('./card') 3 | API_KEY = require('../../API_KEY') 4 | 5 | class CardStackView extends RenderController 6 | 7 | # 8 | # Init 9 | 10 | constructor: (options) -> 11 | super 12 | @collection = options.collection 13 | 14 | @loadData() 15 | 16 | # 17 | # Control 18 | 19 | loadData: -> 20 | @collection.fetch({ headers: {Authorization :"Client-ID #{API_KEY}"} }) 21 | .done(@addCard) 22 | .fail(-> console.log 'card fetch fail') 23 | 24 | addCard: => 25 | cardView = new Card(model: @collection.first()) 26 | cardView.on('card:exit', @onCardExit) 27 | @_eventOutput.emit('card:enter', @collection.first()) 28 | @show(cardView) 29 | 30 | 31 | # 32 | # Events 33 | 34 | onCardExit: (cardModel) => 35 | @collection.remove(cardModel) 36 | @addCard() 37 | 38 | 39 | module.exports = CardStackView 40 | -------------------------------------------------------------------------------- /src/sass/app.css.scss: -------------------------------------------------------------------------------- 1 | @import compass 2 | @import scss-mixins 3 | @import typography 4 | @import famous 5 | 6 | 7 | .card { 8 | border-radius: 8px; 9 | box-shadow: 0 0 6px 8px rgba(0, 0, 0, 0.5); 10 | } 11 | 12 | .card-description { 13 | text-align: center; 14 | font: 16px; 15 | box-shadow: 0 0 6px 8px rgba(0, 0, 0, 0.5); 16 | background-color: rgba(0, 0, 0, .3); 17 | color: white; 18 | } 19 | 20 | .background-image { 21 | -webkit-filter: blur(7px); 22 | z-index: -999; 23 | } 24 | 25 | body { 26 | background-color: black; 27 | } 28 | ; 29 | .menu-bar { 30 | background-color: blue; 31 | z-index: 10; 32 | } 33 | ; 34 | .menu-button { 35 | text-align: center; 36 | font: 16px; 37 | box-shadow: 0 0 6px 8px rgba(0, 0, 0, 0.5); 38 | background-color: rgba(150, 150, 150, .4); 39 | color: black; 40 | line-height: 140px; 41 | } 42 | 43 | .selected { 44 | background-color: rgba(250, 250, 250, .8); 45 | } 46 | 47 | .settings { 48 | color: white; 49 | text-align: center; 50 | } 51 | -------------------------------------------------------------------------------- /src/javascript/views/components/slideout.coffee: -------------------------------------------------------------------------------- 1 | Draggable = require('famous/modifiers/draggable') 2 | StateModifier = require('famous/modifiers/statemodifier') 3 | Transform = require('famous/core/transform') 4 | 5 | class Slideout extends require('famous/core/view') 6 | 7 | # 8 | # Properties 9 | 10 | open: false 11 | 12 | # 13 | # Init 14 | 15 | constructor: -> 16 | super() 17 | 18 | @stateMod = new StateModifier 19 | 20 | @stateMod.setTransform(Transform.translate(0, -300, 0)) 21 | 22 | this._eventInput.on('click', @onClick) 23 | 24 | closeSlideout: -> 25 | @open = false 26 | trans = curve: 'easeOutBounce', duration: 250 27 | @stateMod.setTransform(Transform.translate(0, -300, 0), trans) 28 | 29 | openSlideout: -> 30 | @open = true 31 | trans = curve: 'easeOutBounce', duration: 250 32 | @stateMod.setTransform(Transform.translate(0, 0, 10), trans) 33 | 34 | # 35 | # Events 36 | 37 | onClick: => 38 | if @open then @closeSlideout() else @openSlideout() 39 | 40 | 41 | module.exports = Slideout -------------------------------------------------------------------------------- /src/javascript/views/cards/card-interior.coffee: -------------------------------------------------------------------------------- 1 | ImageSurface = require('famous/surfaces/ImageSurface') 2 | Surface = require('famous/core/surface') 3 | StateModifier = require('famous/modifiers/statemodifier') 4 | 5 | class CardInteriorView extends require('famous/core/view') 6 | 7 | # 8 | # Init 9 | 10 | constructor: (options) -> 11 | super 12 | @model = options.model 13 | 14 | @showCardImage() 15 | @showCardAnnotation() 16 | 17 | 18 | # 19 | # Control 20 | 21 | showCardImage: -> 22 | surface = new ImageSurface 23 | content: @model.getImageSrc() 24 | origin: [0, .5] 25 | size: [undefined, 400] 26 | classes: ['card'] 27 | textAlign: 'center' 28 | 29 | surface.pipe(@._eventOutput) 30 | @add(surface) 31 | 32 | showCardAnnotation: -> 33 | surface = new Surface 34 | content: @model.get('title') 35 | size: [undefined, 60] 36 | classes: ['card-description'] 37 | 38 | state = new StateModifier 39 | align: [.5, .9] 40 | origin: [.5, .5] 41 | 42 | @add(state).add(surface) 43 | 44 | 45 | module.exports = CardInteriorView 46 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Daniel Tello 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. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-starter", 3 | "version": "0.1.1", 4 | "description": "Gulp starter with common tasks and scenarios", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/greypants/gulp-starter.git" 8 | }, 9 | "browser": { 10 | "underscore": "backbone/node_modules/underscore", 11 | "plugin": "./src/javascript/vendor/jquery-plugin.js" 12 | }, 13 | "browserify": { 14 | "transform": [ 15 | "browserify-shim", 16 | "coffeeify", 17 | "hbsfy" 18 | ] 19 | }, 20 | "browserify-shim": { 21 | "plugin": { 22 | "exports": "plugin", 23 | "depends": [ 24 | "jquery:$" 25 | ] 26 | } 27 | }, 28 | "devDependencies": { 29 | "browser-sync": "~0.8.2", 30 | "browserify": "~3.36.0", 31 | "browserify-shim": "~3.4.1", 32 | "coffeeify": "~0.6.0", 33 | "gulp": "^3.8.0", 34 | "gulp-changed": "^0.4.1", 35 | "gulp-imagemin": "^0.6.2", 36 | "gulp-notify": "^1.4.2", 37 | "gulp-ruby-sass": "^0.7.1", 38 | "gulp-util": "^3.0.0", 39 | "handlebars": "^1.3.0", 40 | "hbsfy": "~1.3.2", 41 | "pretty-hrtime": "~0.2.1", 42 | "require-dir": "^0.1.0", 43 | "vinyl-source-stream": "~0.1.1", 44 | "watchify": "~0.10.1" 45 | }, 46 | "dependencies": { 47 | "backbone": "^1.1.2", 48 | "famous": "^0.2.2", 49 | "jquery": "~2.1.0", 50 | "underscore": "^1.6.0", 51 | "handlebars": "~1.3.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/javascript/views/main-view.coffee: -------------------------------------------------------------------------------- 1 | CardStack = require('./cards/card-stack') 2 | ImageSurface = require('famous/surfaces/imagesurface') 3 | Modifier = require('famous/modifiers/statemodifier') 4 | MenuInterior = require('./menu/menu-interior') 5 | Slideout = require('./components/slideout') 6 | 7 | class MainView extends require('famous/core/view') 8 | 9 | # 10 | # Init 11 | 12 | constructor: (options) -> 13 | super 14 | @collection = options.collection 15 | 16 | @createSlideout() 17 | @createContent() 18 | @createBackground() 19 | 20 | 21 | # 22 | # Control 23 | 24 | createContent: -> 25 | content = new CardStack(collection: @collection) 26 | 27 | content.on('card:enter', @onCardEnter) 28 | 29 | @add(content) 30 | 31 | createSlideout: -> 32 | slideout = new Slideout 33 | @add(slideout.stateMod).add(slideout) 34 | 35 | interior = new MenuInterior(collection: @collection) 36 | interior._eventOutput.pipe(slideout._eventInput) 37 | slideout.add(interior) 38 | 39 | createBackground: (model) -> 40 | @background = new ImageSurface 41 | origin: [0, .5] 42 | size: [undefined, undefined] 43 | classes: ['background-image'] 44 | textAlign: 'center' 45 | 46 | mod = new Modifier 47 | opacity: .9 48 | 49 | @add(mod).add(@background) 50 | 51 | setBackground: (model) -> 52 | @background.setContent(model.getImageSrc()) 53 | 54 | 55 | # 56 | # Events 57 | 58 | onCardEnter: (model) => 59 | @setBackground(model) 60 | 61 | 62 | module.exports = MainView 63 | -------------------------------------------------------------------------------- /src/javascript/collections/cards.coffee: -------------------------------------------------------------------------------- 1 | Card = require('../models/card') 2 | Backbone = require('Backbone') 3 | API_KEY = require('../API_KEY') 4 | _ = require('underscore') 5 | 6 | class Cards extends Backbone.Collection 7 | model: Card 8 | urlRoot: "https://api.imgur.com/3/gallery/r/" 9 | url: "https://api.imgur.com/3/gallery/r/pics/time/day/" 10 | category: 'funny' 11 | page: 1 12 | 13 | initialize: -> 14 | @on('sync', @prefetchImages) 15 | @on('remove', @shouldGetNextPage) 16 | @on('change:category', @onCategoryChange) 17 | 18 | parse: (response) -> 19 | response.data 20 | 21 | filterModels: (image) -> 22 | aspectRatio = image.get('width') / image.get('height') 23 | true if aspectRatio > .5 and aspectRatio < 2 24 | 25 | prefetchImages: => 26 | @set(@filter(@filterModels)) 27 | for image,index in @models 28 | break unless image 29 | 30 | if image.get('is_album') 31 | @remove(image) 32 | else 33 | (new Image).src = image.getImageSrc() 34 | 35 | shouldGetNextPage: (options) => 36 | if @length < 15 && !@fetching 37 | @getNextPage() 38 | 39 | onCategoryChange: => 40 | @page = 1 41 | @getNextPage(reset: true) 42 | 43 | resetPageIndex: -> 44 | @page = 1 45 | 46 | getNextPage: (options = {}) => 47 | @fetching = true 48 | 49 | @page++ 50 | 51 | fetchOptions = 52 | headers: {Authorization :"Client-ID #{API_KEY}"} 53 | url: "#{@urlRoot}#{@category}/time/day/#{@page}" 54 | 55 | options = _.extend(options, fetchOptions) 56 | 57 | @fetch(options) 58 | .always( => @fetching = false) 59 | 60 | 61 | module.exports = Cards 62 | -------------------------------------------------------------------------------- /gulp/tasks/browserify.js: -------------------------------------------------------------------------------- 1 | /* browserify task 2 | --------------- 3 | Bundle javascripty things with browserify! 4 | 5 | If the watch task is running, this uses watchify instead 6 | of browserify for faster bundling using caching. 7 | */ 8 | 9 | var browserify = require('browserify'); 10 | var watchify = require('watchify'); 11 | var bundleLogger = require('../util/bundleLogger'); 12 | var gulp = require('gulp'); 13 | var handleErrors = require('../util/handleErrors'); 14 | var source = require('vinyl-source-stream'); 15 | 16 | gulp.task('browserify', function() { 17 | 18 | var bundleMethod = global.isWatching ? watchify : browserify; 19 | 20 | var bundler = bundleMethod({ 21 | // Specify the entry point of your app 22 | entries: ['./src/javascript/app.coffee'], 23 | // Add file extentions to make optional in your requires 24 | extensions: ['.coffee', '.hbs'], 25 | // Enable source maps! 26 | debug: true 27 | }); 28 | 29 | var bundle = function() { 30 | // Log when bundling starts 31 | bundleLogger.start(); 32 | 33 | return bundler 34 | .bundle() 35 | // Report compile errors 36 | .on('error', handleErrors) 37 | // Use vinyl-source-stream to make the 38 | // stream gulp compatible. Specifiy the 39 | // desired output filename here. 40 | .pipe(source('app.js')) 41 | // Specify the output destination 42 | .pipe(gulp.dest('./build/')) 43 | // Log when bundling completes! 44 | .on('end', bundleLogger.end); 45 | }; 46 | 47 | if(global.isWatching) { 48 | // Rebundle with watchify on changes. 49 | bundler.on('update', bundle); 50 | } 51 | 52 | return bundle(); 53 | }); 54 | -------------------------------------------------------------------------------- /src/javascript/views/cards/card.coffee: -------------------------------------------------------------------------------- 1 | CardInterior = require('./card-interior') 2 | Draggable = require('famous/modifiers/draggable') 3 | StateModifier = require('famous/modifiers/statemodifier') 4 | Transform = require('famous/core/transform') 5 | 6 | class CardView extends require('famous/core/view') 7 | 8 | # 9 | # Init 10 | 11 | constructor: (options) -> 12 | super 13 | @model = options.model 14 | @showSurface() 15 | 16 | 17 | # 18 | # Control 19 | 20 | showSurface: -> 21 | cardInterior = new CardInterior 22 | model: @model 23 | dimensions: [1,2] 24 | 25 | @draggable = new Draggable 26 | xRange: [-200, 200] 27 | yRange: [-500, 500] 28 | 29 | @stateMod = new StateModifier 30 | transform: Transform.rotateZ(0) 31 | align: [.5, .075] 32 | origin: [.5, 0] 33 | 34 | cardInterior.pipe(@draggable) 35 | 36 | @draggable.on('end', @onDragEnd) 37 | @draggable.on('update', @onDragUpdate) 38 | 39 | @.add(@stateMod).add(@draggable).add(cardInterior) 40 | 41 | resetPosition: -> 42 | trans = {curve : 'easeOutBounce', duration : 500} 43 | @draggable.setPosition([0,0,0], trans) 44 | @stateMod.setTransform(Transform.identity, trans) 45 | 46 | cardExit: (direction) -> 47 | trans = {curve : 'easeOutBounce', duration : 1500} 48 | 49 | @_eventOutput.emit('card:exit', this.model) 50 | @draggable.setPosition([-600,0,0], trans) if direction == 'left' 51 | @draggable.setPosition([600,0,0], trans) if direction == 'right' 52 | 53 | 54 | # 55 | # Events 56 | 57 | onDragUpdate: => 58 | @stateMod.setTransform(Transform.rotateZ(@draggable.getPosition()[0]/800)) 59 | 60 | onDragEnd: => 61 | distance = @draggable.getPosition()[0] 62 | 63 | if distance > 150 64 | @cardExit('right') 65 | else if distance < -150 66 | @cardExit('left') 67 | else 68 | @resetPosition() 69 | 70 | 71 | module.exports = CardView 72 | 73 | -------------------------------------------------------------------------------- /src/sass/_famous.sass: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | * 5 | * Owner: mark@famo.us 6 | * @license MPL 2.0 7 | * @copyright Famous Industries, Inc. 2014 8 | */ 9 | 10 | 11 | html 12 | width: 100% 13 | height: 100% 14 | margin: 0px 15 | padding: 0px 16 | overflow: hidden 17 | -webkit-transform-style: preserve-3d 18 | transform-style: preserve-3d 19 | 20 | 21 | body 22 | position: absolute 23 | width: 100% 24 | height: 100% 25 | margin: 0px 26 | padding: 0px 27 | -webkit-transform-style: preserve-3d 28 | transform-style: preserve-3d 29 | -webkit-font-smoothing: antialiased 30 | -webkit-tap-highlight-color: transparent 31 | -webkit-perspective: 0 32 | perspective: none 33 | overflow: hidden 34 | 35 | 36 | .famous-container, .famous-group 37 | position: absolute 38 | top: 0px 39 | left: 0px 40 | bottom: 0px 41 | right: 0px 42 | overflow: visible 43 | -webkit-transform-style: preserve-3d 44 | transform-style: preserve-3d 45 | -webkit-backface-visibility: visible 46 | backface-visibility: visible 47 | pointer-events: none 48 | 49 | 50 | .famous-group 51 | width: 0px 52 | height: 0px 53 | margin: 0px 54 | padding: 0px 55 | -webkit-transform-style: preserve-3d 56 | transform-style: preserve-3d 57 | 58 | 59 | .famous-surface 60 | position: absolute 61 | -webkit-transform-origin: center center 62 | transform-origin: center center 63 | -webkit-backface-visibility: hidden 64 | backface-visibility: hidden 65 | -webkit-transform-style: flat 66 | transform-style: preserve-3d 67 | -webkit-box-sizing: border-box 68 | -moz-box-sizing: border-box 69 | -webkit-tap-highlight-color: transparent 70 | pointer-events: auto 71 | 72 | 73 | .famous-container-group 74 | position: relative 75 | width: 100% 76 | height: 100% 77 | -------------------------------------------------------------------------------- /src/javascript/views/menu/menu-interior.coffee: -------------------------------------------------------------------------------- 1 | _ = require('underscore') 2 | Surface = require('famous/core/surface') 3 | GridLayout = require('famous/views/gridlayout') 4 | StateModifier = require('famous/modifiers/statemodifier') 5 | MenuButton = require('./menu-button') 6 | Transform = require('famous/core/transform') 7 | 8 | class MenuInterior extends require('famous/core/view') 9 | 10 | # 11 | # Properties 12 | 13 | filters: 14 | [ 15 | { 16 | name: 'Everything' 17 | value: 'pics' 18 | }, 19 | { 20 | name: 'Funny' 21 | value: 'funny' 22 | }, 23 | { 24 | name: 'Mildly Interesting' 25 | value: 'mildlyinteresting' 26 | }, 27 | { 28 | name: 'Gifs' 29 | value: 'gifs' 30 | } 31 | ] 32 | 33 | # 34 | # Init 35 | 36 | constructor: (options) -> 37 | super 38 | @collection = options.collection 39 | 40 | @createMenu() 41 | @createClose() 42 | 43 | 44 | # 45 | # Control 46 | 47 | createMenu: -> 48 | stateMod = new StateModifier 49 | size: [undefined, 300] 50 | 51 | grid = new GridLayout 52 | dimensions: [2,2] 53 | 54 | @surfaces = [] 55 | grid.sequenceFrom(@surfaces) 56 | 57 | for filter in @filters 58 | surface = new MenuButton 59 | value: filter.value 60 | content: filter.name 61 | classes: ['menu-button'] 62 | 63 | 64 | surface.on('button:clicked', @onCategoryClick) 65 | surface.filter = filter 66 | @surfaces.push surface 67 | 68 | _.findWhere(@surfaces, content: 'Everything').addClass('selected') 69 | 70 | @add(stateMod).add(grid) 71 | 72 | createClose: -> 73 | stateMod = new StateModifier 74 | transform: Transform.translate(0, 300, 0) 75 | 76 | surface = new Surface 77 | size: [undefined, 200] 78 | content: 'Settings' 79 | classes: ['settings'] 80 | 81 | surface.on('click', @onCloseClick) 82 | 83 | @add(stateMod).add(surface) 84 | 85 | 86 | # 87 | # Events 88 | 89 | onCloseClick: => 90 | @_eventOutput.trigger('click') 91 | 92 | onCategoryClick: (category) => 93 | _.each(@surfaces, (surface) -> surface.removeClass('selected')) 94 | _.findWhere(@surfaces, value: category).addClass('selected') 95 | 96 | @collection.category = category 97 | @collection.resetPageIndex() 98 | @collection.getNextPage() 99 | 100 | module.exports = MenuInterior --------------------------------------------------------------------------------