4 juin 2014

Meteor docset is available in Dash

Docsets are offline and searchable docs that you can browse using Dash. Recently, Meteor's docset has been added. Now, even when you are offline, you can easily browse your favorite framework's docs.

You can even call Dash within Vim using the Vimdash plugin.

1 juin 2014

Velocity.js and Meteor

Introduction

Velocity.js is a new animation library in JS. It acts as a replacement of jQuery's animation library. Internally, it uses the CSS3 transformation and animation properties leveraging their hardware acceleration capabilities where possible and falling back on basic jQuery's animation when they are not available. Neat. Here is a small video of a simple animation developed by its creator:
Doing front end development with Meteor is so easy that I've recreated this demo and deployed it in less than one hour. Here is the result: http://velocity3ddemo.meteor.com/

Step-by-step recreation

Create a basic Meteor application:
mrt create Velocity3dDemo
cd Velocity3dDemo
rm -rf Velocity3dDemo*
Import packages:
mrt add coffeescript
mrt add jade
mrt add stylus
mrt add velocityjs
Create a basic web app structure:
mkdir -p client/stylesheets
Create the Stylus file client/stylesheets/app.styl:
@import 'nib'

*
  padding: 0
  margin: 0

body
  background-color: #060b14
  overflow: hidden
  color: #ffffff
  font-family: Helvetica Neue, Open Sans, sans-serif
  font-weight: 100

a
  color: #4bc2f1
  text-decoration: none
  &:hover
    text-decoration: underline

#container
  perspective: 50px
  transform-origin: 50% 100%
  pointer-events: none
  opacity: 0.55

#welcome
  position: fixed
  width: 22rem
  left: 50%
  top: 45%
  margin-top: -1rem
  margin-left: -11rem
  font-weight: 200
  opacity: 0.65
  text-align: center
  font-size: 0.775rem
  line-height: 1.05rem
  letter-spacing: 0.135em
  word-spacing: -0.075rem

@media screen and (max-width: 400px)
  #welcome
    font-size: 0.45rem !important

#logo
  position: fixed
  right: 0.75rem
  bottom: 0.65rem
  cursor: pointer
  text-decoration: none
  color: #d6d6d6
  font-size: 2rem
  letter-spacing: 0.025em

#logoDot
  color: #d74580

.dot
  position: fixed
  width: 30px
  height: 30px
  border-radius: 30px
  background-color: #4bc2f1
Create the Jade file client/index.jade:
head
  title Velocity 3D demo
body
  +index

template(name='index')
  #welcome No WebGL. No Canvas. Just pure DOM.
  a#logo(href='http://julian.com/research/velocity/') Velocity.js
  #container
Create the CoffeeScript file client/index.coffee:
Template.index.rendered = ->
  # Device detection
  isWebkit = /Webkit/i.test navigator.userAgent
  isChrome = /Chrome/i.test navigator.userAgent
  isMobile = window.ontouchstart isnt undefined
  isAndroid = /Android/i.test navigator.userAgent
  isIE = document.documentMode

  # Redirection
  if isMobile and isAndroid and not isChrome
    alert 'Use Chrome on Android'

  # Helpers
  # Randomly generate an integer between 2 numbers.
  r = (min, max) ->
    Math.floor(Math.random() * (1 + max - min)) + min

  # Dot creation
  # Differentiate dot counts on based on device and browser capabilities
  dotsCount = if isMobile then (if isAndroid then 40 else 60) else ( if isChrome then 175 else 125)
  dotsHtml = ''
  for i in [0..dotsCount]
    dotsHtml += '<div class="dot"></div>'
  $dots = $ dotsHtml

  # Setup
  $container = $ '#container'
  $welcome = $ '#welcome'

  screenWidth = window.screen.availWidth
  screenHeight = window.screen.availHeight
  chromeHeight = screenHeight - (document.documentElement.clientHeight or screenHeight)

  translateZMin = - 725
  translateZMax = 600

  containerAnimationMap =
    perspective: [215, 50]
    opacity: [0.90, 0.55]

  # IE10+ produce odd glitching issues when you rotateZ on a parent element subjected to 3D transforms.
  containerAnimationMap.rotateZ = [5, 0] if not(isIE)

  # Animation
  # Fade out the welcome message.
  $welcome.velocity
      opacity: [0, 0.65]
    ,
      display: 'none'
      delay: 3500
      duration: 1100
  # Animate the dot's container.
  $container
    .css 'perspective-origin', "#{screenWidth/2}px #{(screenHeight*0.45)-chromeHeight}px"
    .velocity containerAnimationMap, {duration: 800, loop: 1, delay: 3250}

  # Special visual enhancement for WebKit browsers which are faster at box-shadow manipulation
  ($dots.css 'boxShadow', '0px 0px 4px 0px #4bc2f1') if isWebkit

  $dots
    .velocity
        translateX: [
          -> '+=' + r -screenWidth/2.5, screenWidth/2.5
          -> r 0, screenWidth
        ]
        translateY: [
          -> '+=' + r -screenHeight/2.75, screenHeight/2.75
          -> r 0, screenHeight
        ]
        translateZ: [
          -> '+=' + r translateZMin, translateZMax
          -> r translateZMin, translateZMax
        ]
        opacity: [
          -> Math.random()
          -> Math.random() + 0.1
        ]
      ,
        duration: 6000
        easing: 'easeInOutsine'
    .velocity 'reverse', {easing: 'easeOutQuad'}
    .velocity
        opacity: 0
      ,
        duration: 2000
        complete: ->
          $welcome
            .html "<a href='https://www.youtube.com/watch?v=MDLiVB6g2NY&hd=1'>Watch the making of this demo.</a><br /><br />Go create something amazing.<br />Sincerely, <a href='http://twitter.com/shapiro'>@Shapiro</a>"
            .velocity
                opacity: 0.75
              ,
                duration: 3500
                display: 'block'
    .appendTo $container
Launch it:
mrt
Publish it:
mrt deploy velocity3ddemo.meteor.com
Side note: I've just redeploy this little web app with a link to Meteor. My browser was on the page. Automatically, my browser has been informed that a new release of the code was available. It has only reloaded the modified assets. This is the incredible power of the live reload even when your apps are deployed. Amazing.

The original tutorial

Julian Shapiro, the author of Velocity.js, has provided a very nice Youtube channel which describes how he achieves his demo. A must watch.

31 mai 2014

htop, a better CLI process viewer

Inside my CLI, I sometime like to see which process eats up my CPU or my RAM. top is generally a nice utility for that kind of tasks. But, there is a better and cleaner alternative: htop. A comparison picture is worth a thousand words.
Install it on OSX using:
brew install htop

25 mai 2014

Famo.us polaroid tutorial in CoffeeScript and within Meteor

Introduction

Since Famo.us v0.2.0, some new tutorials have been added to the Famo.us University. The 1st available project is a Polaroid tutorial which teaches you how to create your own app and widget with Famo.us. Just play this little video to check how incredible the animations are with plain simple HTML5 technologies. Note that, this example is completely responsive and I've tested it on smartphones, tablets and desktops.
This example project is not just beautiful. It works very well in iOS, Chrome on Android, Chrome, Safari and Opera. There are little flickers on Firefox but it seems more due to the browser than to the framework. Though, it is completely useable. As ever, the stock Android browser is definitely a knightmare. Meaning that, if you plan on encapsulating your WebApp in a native browser, Apache Cordova is the way to go for iOS but you will need to encapsulate Chromium if you plan on shipping your WebApp for Android. This can be achieve with projects such as Crosswalk, for instance.

Though, the tutorial is incredibly nice and goes smoothly over every difficulties that you may encounter (one of the best tutorial, that I've seen so far), I didn't choose the same way as described. Actually, JavaScript's inheritance model is such a pain to write, that I prefer avoiding it as much as I can. Thus, I've recreated it using another set of tools and it runs as smoothly as the original:
The result is a very small set of code that produces the same WebApp in a fraction of the necessary code and installation steps.

Note that the following tutorial is not a replacement of the one from the Famo.us University. It is a complement to show how this framework is easily integrable with other powerful HTML5 technologies.

Configuring your Meteor project

Create your Meteor project with the dead simple following command:
meteor create Polaroid
cd Polaroid
Now create a common fullstack JS directory structure only targeted for a client WebApp without server side integration:
mkdir -p client/lib client/models client/startup client/stylesheets client/views lib packages public/img
Remove the automatically created files:
rm -rf Polaroid.*
Add the following package with Meteor:
meteor add coffeescript
meteor add stylus
And this one with Meteorite:
mrt add jade
mrt add famono
Easy as pie.

Get the unique required asset from Famo.us

This example WebApp needs a simple public/img/camera.png that you'll find in the Zip file that Famo.us provides in their download section.

Create your style file

The style file client/stylesheets/app.styl is kept as its minimum as most of the CSS rules are handled by Famo.us.
@import 'nib'

html
  background: #404040

body
  -webkit-touch-callout: none
  user-select: none
  font-family: 'AvenirNext-Medium'

Create your HTML file with Jade

As before for the style file, the main HTML file client/index.jade is kept as its minimum as most of the tags are handled by Famo.us
head
  title Famo.us Polaroid
body
  +index

template(name='index')
That's it, a simple template loaded by Meteor.

Create some namespaces

I like to isolate my code from the code that I import. Thus, I create 2 namespaces in 2 separate files.
  • One is dedicated to the WebApp in client/lib/polaroid.coffee:
    # Declare Polaroid namespace
    window.Polaroid ?= {}
    
  • The other one is dedicated to Famo.us in client/lib/famous.coffee:
    # Declare Famo.us namespace
    window.Famous ?= {}
    
The lib directory is used as it is loaded first by Meteor.

The model, a Picasa album

With this example, we do not leverage the power of the full JS stack that Meteor provides. We only use its features of live reloading the code and its easy to use build capability. The model is the same as the one provided in the Famo.us Zip file that you've downloaded except that it is created as a CoffeeScript file named client/models/slidedata.coffee:
Polaroid.SlideData =
  userId: "109813050055185479846"
  albumId: "6013105701911614529"
  picasaUrl: "https://picasaweb.google.com/data/feed/api/user/"
  queryParams: "?alt=json&hl=en_US&access=visible&fields=entry(id,media:group(media:content,media:description,media:keywords,media:title))"
  defaultImage: "https://lh4.googleusercontent.com/-HbYp2q1BZfQ/U3LXxmWoy7I/AAAAAAAAAJk/VqI5bGooDaA/s1178-no/1.jpg"
  getUrl: ->
    @picasaUrl + @userId + "/albumid/" + @albumId + @queryParams
  parse: (data) ->
    urls = []
    data = JSON.parse(data)
    entries = data.feed.entry
    i = 0

    while i < entries.length
      media = entries[i].media$group
      urls.push media.media$content[0].url
      i++
    urls
A simple dictionary with 2 methods.

Requiring the Famo.us libraries

I simply load all the Famo.us libraries in a single location. This drastically reduces the amount of code. I use the same file client/startup/famous.coffee to load the polyfills and to create the Famo.us's singleton so that if I enhance this WebApp with multiple page loaded with a router, there will be no additional loadings or instantiations.
# Import famous.css
require 'famous/core/famous'
# Adds the famo.us dependencies
require 'famous-polyfills'
# Wait for document ready
$(document).ready ->
  # Load Famo.us libraries
  Famous.Engine           = require 'famous/core/Engine'
  Famous.View             = require 'famous/core/View'
  Famous.Transform        = require 'famous/core/Transform'
  Famous.Surface          = require 'famous/core/Surface'
  Famous.StateModifier    = require 'famous/modifiers/StateModifier'
  Famous.Timer            = require 'famous/utilities/Timer'
  Famous.ImageSurface     = require 'famous/surfaces/ImageSurface'
  Famous.ContainerSurface = require 'famous/surfaces/ContainerSurface'
  Famous.Lightbox         = require 'famous/views/Lightbox'
  Famous.Utility          = require 'famous/utilities/Utility'
  Famous.Easing           = require 'famous/transitions/Easing'
  Famous.ContainerSurface = require 'famous/surfaces/ContainerSurface'
  Famous.Transitionable   = require 'famous/transitions/Transitionable'
  Famous.SpringTransition = require 'famous/transitions/SpringTransition'
  # Register transitions
  Famous.Transitionable.registerMethod 'spring', Famous.SpringTransition
  # Create main context
  Polaroid.mainCtx = Famous.Engine.createContext()

Instantiate the main template

The content of the main template is set under the client/index.coffee that goes along with our Jade file:
Template.index.rendered = ->
  # Get request to Picasa API
  Famous.Utility.loadURL Polaroid.SlideData.getUrl(), (data) ->
    data = Polaroid.SlideData.parse data
    # Instantiate the AppView with our URL's data
    Polaroid.appView = new Polaroid.AppView data: data
    Polaroid.mainCtx.add Polaroid.appView

Creating the views

The rest of this tutorial is pretty close to the one from the Famo.us University except that it uses the object model provided by CoffeeScript instead of the one from JavaScript. You should fill this 3 files while following the tutorial from the Famo.us University so that you get the nice explanations that they provided us.

The views are composed of 3 files:
  • client/views/appview.coffee
    $(document).ready ->
    
      class Polaroid.AppView extends Famous.View
        DEFAULT_OPTIONS:
          data: undefined
          cameraWidth: 0.6 * window.innerHeight
        constructor: (@options) ->
          @DEFAULT_OPTIONS.slideWidth = 0.8 * @DEFAULT_OPTIONS.cameraWidth
          @DEFAULT_OPTIONS.slideHeight = @DEFAULT_OPTIONS.slideWidth + 40
          @DEFAULT_OPTIONS.slidePosition = 0.77 * @DEFAULT_OPTIONS.cameraWidth
          @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
          super @options
          @createCamera()
          @createSlideshow()
    
        createCamera: ->
          camera = new Famous.ImageSurface
            size: [@options.cameraWidth, true]
            content: 'img/camera.png'
            properties:
              width: '100%'
          cameraModifier = new Famous.StateModifier
            origin: [0.5, 0]
            align: [0.5, 0]
            transform: Famous.Transform.behind
          @add(cameraModifier).add camera
    
        createSlideshow: ->
          slideshowView = new Polaroid.SlideshowView
            size: [@options.slideWidth, @options.slideHeight]
            data: @options.data
          slideshowModifier = new Famous.StateModifier
            origin: [0.5, 0]
            align: [0.5, 0]
            transform: Famous.Transform.translate 0, @options.slidePosition, 0
          slideshowContainer = new Famous.ContainerSurface
            properties:
              overflow: 'hidden'
          @add(slideshowModifier).add slideshowContainer
          slideshowContainer.add slideshowView
          slideshowContainer.context.setPerspective 1000
    
  • client/views/slideshowview.coffee
    $(document).ready ->
    
      class Polaroid.SlideshowView extends Famous.View
        DEFAULT_OPTIONS:
          size: [450, 500]
          data: undefined
          lightboxOpts:
            inOpacity: 1
            outOpacity: 0
            inOrigin: [0, 0]
            outOrigin: [0, 0]
            showOrigin: [0, 0]
            inTransform: Famous.Transform.thenMove Famous.Transform.rotateX(0.9), [0, -300, -300]
            outTransform: Famous.Transform.thenMove Famous.Transform.rotateZ(0.7), [0, window.innerHeight, -1000]
            inTransition: duration: 500, curve: Famous.Easing.outBack
            outTransition: duration: 350, curve: Famous.Easing.inQuad
    
        constructor: (@options) ->
          @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
          super @options
          @rootModifier = new Famous.StateModifier
            size: @options.size
            origin: [0.5, 0]
            align: [0.5, 0]
          @mainNode = @add @rootModifier
          @createLightbox()
          @createSlides()
    
        createLightbox: ->
          @lightbox = new Famous.Lightbox @options.lightboxOpts
          @mainNode.add @lightbox
    
        createSlides: =>
          @slides = []
          @currentIndex = 0
          console.log @options.data
          for url in @options.data
            slide = new Polaroid.SlideView
              size: @options.size
              photoUrl: url
            @slides.push slide
            slide.on 'click', @showNexSlide
          @showCurrentSlide()
    
        showCurrentSlide: ->
          @ready = false
          slide = @slides[@currentIndex]
          @lightbox.show slide, =>
            @ready = true
            slide.fadeIn()
    
        showNexSlide: =>
          return if @ready isnt true
          @currentIndex++
          if @currentIndex is @slides.length
            @currentIndex = 0
          @showCurrentSlide()
    
  • client/views/slideview.coffee
    $(document).ready ->
    
      class Polaroid.SlideView extends Famous.View
        DEFAULT_OPTIONS:
          size: [400, 450]
          filmBorder: 15
          photoBorder: 3
          photoUrl: Polaroid.SlideData.defaultImage
          angle: -0.5
    
        constructor: (@options) ->
          @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
          super @options
          @rootModifier = new Famous.StateModifier
            size: @options.size
          @mainNode = @add @rootModifier
          @createBackground()
          @createFilm()
          @createPhoto()
    
        createBackground: ->
          background = new Famous.Surface
            properties:
              backgroundColor: '#fffff5'
              boxShadow: '0 10px 20px -5px rgba(0, 0, 0, 0.5)'
              cursor: 'pointer'
          @mainNode.add background
          background.on 'click', =>
            @_eventOutput.emit 'click'
    
        createFilm: ->
          @options.filmSize = @options.size[0] - 2 * @options.filmBorder
          film = new Famous.Surface
            size: [@options.filmSize, @options.filmSize]
            properties:
              backgroundColor: '#222'
              zIndex: 1
              # Make surface invisible to pointer events
              pointerEvents: 'none'
          filmModifier = new Famous.StateModifier
            origin: [0.5, 0]
            align: [0.5, 0]
            transform: Famous.Transform.translate 0, @options.filmBorder, 1
          @mainNode
            .add filmModifier
            .add film
    
        createPhoto: ->
          photoSize = @options.filmSize - 2 * @options.photoBorder
          photo = new Famous.ImageSurface
            size: [photoSize, photoSize]
            content: @options.photoUrl
            properties:
              zIndex: 2
              # Make surface invisible to pointer events
              pointerEvents: 'none'
          @photoModifier = new Famous.StateModifier
            origin: [0.5, 0]
            align: [0.5, 0]
            transform: Famous.Transform.translate 0, @options.filmBorder + @options.photoBorder, 2
            opacity: 0.01
          @mainNode
            .add @photoModifier
            .add photo
    
        fadeIn: =>
          @photoModifier.setOpacity 1, {duration: 1500, curve: 'easeIn'}
          @shake()
    
        shake: ->
          @rootModifier.halt()
          @rootModifier.setTransform Famous.Transform.rotateX(@options.angle), {duration: 200, curve: 'easeOut'}
          @rootModifier.setTransform Famous.Transform.identity, {method: 'spring', period: 600, dampingRatio: 0.15}
    

Further words

My integration is not as proper as I would like it to be. Each class declaration relies on a ready event that I did not succeed in removing. Identically, my class constructors call their parent's one explicitly. Feel free to post some comments if you have a better integration.

Preview markdown in your finder

Sidre Sorhus has created an interesting set of QuickLook plugins for the OSX's Finder. Among them, there is a Markdown previewer. Very handy. Install these plugins with this command:
brew cask install qlcolorcode qlstephen qlmarkdown quicklook-json qlprettypatch quicklook-csv betterzipql webp-quicklook suspicious-package

22 mai 2014

Never cry again after an unfortunate "rm -rf"

Sidresorhus has just published an awesome package named trash. This little CLI command delete your files and folders by moving them into your OS's trashcan. Beside being a secure rm command, it simplifies it when you are removing a tree of files. Super nice.

It supports Windows, OSX and Linux.

18 mai 2014

The Cult of Gulp: Independent preprocessor for Gulp

In a former post (A gulp of coffee: your gulpfile in coffeescript), I've shown how to bootstrap CoffeeScript in Gulp. There is now a far better solution than mine that does not require any additional file: Cult.

This little CLI just analyse the extension and automatically requires the right REPL. A time saver!