Sunday, December 13, 2015

React & Flux Architecture with Clojurescript

React & Flux Architecture with Clojurescript


Over the last few months I’ve been using Clojure and Clojurescript for full stack development. I’m particularly taken with using Reagent, a minimal implementation of React in Clojurescript.

Install Leiningen

First we need to install the Clojure automation library leiningen, which I thoroughly recommend for using with all your Clojure projects.

Install Leiningen (copied from leiningen website)

Download the lein script (or on Windows lein.bat)
Place it on your $PATH where your shell can find it (eg. ~/bin)
Set it to be executable (chmod a+x ~/bin/lein)
Run it (lein) and it will download the self-install package

Create a project

Now we will create a Reagent project to get us started, in your terminal type

lein new reagent test_project
cd test_project

to run the project we just type

lein figwheel

Leiningen will download and install all the project dependencies (defined in packages.json), and run a hot-loaded development version of your project.
You should see something like this

Retrieving lein-figwheel/lein-figwheel/0.5.0-2/lein-figwheel-0.5.0-2.pom from clojars
Retrieving figwheel-sidecar/figwheel-sidecar/0.5.0-2/figwheel-sidecar-0.5.0-2.pom from clojars
Retrieving http-kit/http-kit/2.1.19/http-kit-2.1.19.pom from clojars
Retrieving figwheel/figwheel/0.5.0-2/figwheel-0.5.0-2.pom from clojars
Retrieving figwheel-sidecar/figwheel-sidecar/0.5.0-2/figwheel-sidecar-0.5.0-2.jar from clojars
Retrieving lein-figwheel/lein-figwheel/0.5.0-2/lein-figwheel-0.5.0-2.jar from clojars
Retrieving http-kit/http-kit/2.1.19/http-kit-2.1.19.jar from clojars
Retrieving figwheel/figwheel/0.5.0-2/figwheel-0.5.0-2.jar from clojars
Retrieving org/clojure/core.async/0.2.374/core.async-0.2.374.pom from central
Retrieving org/clojure/tools.analyzer.jvm/0.6.9/tools.analyzer.jvm-0.6.9.pom from central
Retrieving org/clojure/tools.analyzer/0.6.7/tools.analyzer-0.6.7.pom from central
Retrieving org/clojure/core.memoize/0.5.8/core.memoize-0.5.8.pom from central
Retrieving org/clojure/core.cache/0.6.4/core.cache-0.6.4.pom from central
Retrieving org/clojure/data.priority-map/0.0.4/data.priority-map-0.0.4.pom from central
Retrieving org/clojure/tools.reader/1.0.0-alpha1/tools.reader-1.0.0-alpha1.pom from central
Retrieving org/clojure/tools.analyzer/0.6.7/tools.analyzer-0.6.7.jar from central
Retrieving org/clojure/tools.analyzer.jvm/0.6.9/tools.analyzer.jvm-0.6.9.jar from central
Retrieving org/clojure/core.cache/0.6.4/core.cache-0.6.4.jar from central
Retrieving org/clojure/core.memoize/0.5.8/core.memoize-0.5.8.jar from central
Retrieving org/clojure/core.async/0.2.374/core.async-0.2.374.jar from central
Retrieving org/clojure/data.priority-map/0.0.4/data.priority-map-0.0.4.jar from central
Figwheel: Starting server at http://localhost:3449
Figwheel: Watching build - app
Compiling "target/cljsbuild/public/js/app.js" from ("src/cljs" "src/cljc" "env/dev/cljs")...
Successfully compiled "target/cljsbuild/public/js/app.js" in 9.736 seconds.
Figwheel: Starting CSS Watcher for paths  ["resources/public/css"]
Figwheel: Starting nREPL server on port: 7002
Launching ClojureScript REPL for build: app
Figwheel Controls:
          (stop-autobuild)                ;; stops Figwheel autobuilder
          (start-autobuild [id ...])      ;; starts autobuilder focused on optional ids
          (switch-to-build id ...)        ;; switches autobuilder to different build
          (reset-autobuild)               ;; stops, cleans, and starts autobuilder
          (reload-config)                 ;; reloads build config and resets autobuild
          (build-once [id ...])           ;; builds source one time
          (clean-builds [id ..])          ;; deletes compiled cljs target files
          (print-config [id ...])         ;; prints out build configurations
          (fig-status)                    ;; displays current state of system
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: Control+C or :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application
To quit, type: :cljs/quit
cljs.user=>

Now open your browser to http://localhost:3449 to view your new reagent project.
The command above has generated boiler-plate code for our reagent project and gives us a little helping hand to get us started.

Improve the routing

So, the reagent seed project gives you one file src/cljs/test_project/core.cljs
, which contains initialization of the app, view definitions and routes. Let’s move the routing out to a separate file, to make it easier to extend. Here is what you should be starting with in core.cljs

; src/cljs/test-project/core.cljs
(ns test-project.core
  (:require [reagent.core :as reagent :refer [atom]]
            [reagent.session :as session]
            [secretary.core :as secretary :include-macros true]
            [accountant.core :as accountant]))

;; -------------------------
;; Views

(defn home-page []
  [:div [:h2 "Welcome to test_project"]
   [:div [:a {:href "/about"} "go to about page"]]])

(defn about-page []
  [:div [:h2 "About test_project"]
   [:div [:a {:href "/"} "go to the home page"]]])

(defn current-page []
  [:div [(session/get :current-page)]])

;; -------------------------
;; Routes

(secretary/defroute "/" []
  (session/put! :current-page #'home-page))

(secretary/defroute "/about" []
  (session/put! :current-page #'about-page))

;; -------------------------
;; Initialize app

(defn mount-root []
  (reagent/render [current-page] (.getElementById js/document "app")))

(defn init! []
  (accountant/configure-navigation!)
  (accountant/dispatch-current!)
  (mount-root))

So let’s trim down the core so you just have

; src/cljs/test-project/core.cljs
(ns test-project.core
  (:require [reagent.core :as reagent :refer [atom]]
            [reagent.session :as session]
            [secretary.core :as secretary :include-macros true]
            [accountant.core :as accountant]
            [test-project.routes]))

(defn current-page []
  [:div [(session/get :current-page)]])

(defn mount-root []
  (reagent/render [current-page] (.getElementById js/document "app")))

(defn init! []
  (accountant/configure-navigation!)
  (accountant/dispatch-current!)
  (mount-root))

We now need to create a file to put the routes in called routes.cljs, in the same directory as the core.cljs .. and let’s put the routing from core in there

; src/cljs/test-project/routes.cljs
(ns test-project.routes
  (:require [reagent.session :as session]
            [secretary.core :as secretary :include-macros true]
            [test-project.pages.home :refer [home-page]]
            [test-project.pages.about :refer [about-page]]))

(secretary/defroute "/" []
  (session/put! :current-page #'home-page))

(secretary/defroute "/about" []
  (session/put! :current-page #'about-page))

We’re also going to create a subfolder called pages and put the 2 view components in there, which we’ll call home.cljs and about.cljs respectively:

; src/cljs/test-project/pages/home.cljs
(ns test-project.pages.home
  (:require [reagent.core :as reagent :refer [atom]]))

(defn home-page []
  [:div [:h2 "Welcome to test_project"]
   [:div [:a {:href "/about"} "go to about page"]]])
; src/cljs/test-project/pages/about.cljs
(ns test-project.pages.about
  (:require [reagent.core :as reagent :refer [atom]]))

(defn about-page []
  [:div [:h2 "About test_project"]
   [:div [:a {:href "/"} "go to the home page"]]])

You may need to quit and restart lein figwheel to get it to find the new files in the hot-reloader. If you refresh the browser it should look and function as before.

Create a Flux store

so we have a bare-bones project that generates two pages; a home page and an about page, and it allows us to navigate between them.

We want to create a store for the app, so that our components can respond to changes in the store as a single source of truth for the app.
We use the reagent atom to store application state, it behaves like the standard clojure atom, except that when a component dereferences it, it forces a re-render of the component, as a React component does when props or state changes.
Let’s create a store in the same folder as our core called store.cljs and store a variable for the title of each page

; src/cljs/test-project/store.cljs
(ns test-project.store
  (:require [reagent.core :as reagent :refer [atom]]))

(def app-title (atom "Test Project"))

and now we can access it in our page components by requiring and dereferencing the store atom like this

; src/cljs/test-project/pages/about.cljs
(ns test-project.pages.about
  (:require [reagent.core :as reagent :refer [atom]]
            [test-project.store :refer [app-title]]))

(defn about-page []
  [:div [:h2 "About " @app-title]
   [:div [:a {:href "/"} "go to the home page"]]])

and

; src/cljs/test-project/pages/home.cljs
(ns test-project.pages.home
  (:require [reagent.core :as reagent :refer [atom]]
            [test-project.store :refer [app-title]]))

(defn home-page []
  [:div [:h2 "Welcome to " @app-title]
   [:div [:a {:href "/about"} "go to about page"]]])

Handle Flux actions properly

Now this is where we will introduce a new concept. Most implementations using Reagent will simply reset! and deref the store atoms to implement a Flux-style architecture. However, Flux requires the use of a dispatcher, which manages the sequencing of actions to the stores. We already have the functionality for the components to listen to the stores and respond to updates, but we don’t yet have a robust mechanism for sending actions to multiple stores. Let’s implement an actions mechanism by creating actions.cljs in the core folder. This doesn’t need to use reagent as it won’t be forcing any rendering in components.

; src/cljs/test-project/actions.cljs
(ns test-project.actions)

(def dispatcher (atom {}))
(def actions (atom {}))

(defn emit [action data]
  (swap! dispatcher #(merge {:action action} data)))

(defn register [action callback]
  (swap! actions conj {action callback}))

(add-watch dispatcher :watcher
  (fn [key atom old-state new-state]
    (if-let [action (get @actions (:action new-state))]
      (action new-state))))

This allows us to emit an action from anywhere in the app, by naming the action and sending a map of data that our store will use when processing the action.
So our store now needs to implement a specific action, e.g. store.cljs becomes

; src/cljs/test-project/store.cljs
(ns test-project.store
  (:require [reagent.core :as reagent :refer [atom]]
            [test-project.actions :as actions]))

(def app-title (atom "Test Project"))

(defn update-title [data]
  (reset! app-title (:value data)))

(actions/register :update-title update-title)

, which just updates the title when an action called :update-title is emitted and has a map containing the new value.

We can now call this action from a component. We’ll create an input box on the home page that sends an action to update the title when it is changed. Our home page becomes

; src/cljs/test-project/pages/home.cljs
(ns test-project.pages.home
  (:require [reagent.core :as reagent :refer [atom]]
            [test-project.store :refer [app-title]]
            [test-project.actions :as actions]))

(defn update-text [evt]
  (actions/emit :update-title {:value (-> evt .-target .-value)}))

(defn home-page []
  [:div [:h2 "Welcome to " @app-title]
    [:div [:a {:href "/about"} "go to about page"]]
    [:div "change title:"
      [:input {:value @app-title :on-change update-text}]]])

We should now be able to change the text in the input box on the home page, and dynamically see the title update on the home page, and the title should appear changed when we navigate to the about page.

Thursday, April 16, 2015

Clojure Tutorial 1

My First Clojure script

After some immersion in FRP concepts in Javascript I decided to take the leap and start learning Clojure the popular functional programming language that runs on the Java Virtual Machine (JVM). There is also a great compiler for it to Javascript and many are starting to use Clojurescript to write declarative web-apps.

Here I am going to introduce you to Clojure by means of an example that we can run from the command line.


The problem

For this simple example I am going to implement a script to test an integer passed in from the command line, to see if it is a perfect number or not. A perfect number is a composite integer whose factors, when summed, are equal to the integer itself.

Installation

First we need to install the Clojure automation library leiningen, which I thoroughly recommend for using with all your Clojure projects.

Install Leiningen (copied from leiningen website)

  • Download the lein script (or on Windows lein.bat)
  • Place it on your $PATH where your shell can find it (eg. ~/bin)
  • Set it to be executable (chmod a+x ~/bin/lein)
  • Run it (lein) and it will download the self-install package

now we must install lein-exec to run our script from the command-line

 wget https://raw.github.com/kumarshantanu/lein-exec/master/lein-exec  
 wget https://raw.github.com/kumarshantanu/lein-exec/master/lein-exec-p  
 wget https://raw.github.com/kumarshantanu/lein-exec/master/project.clj  
 chmod a+x lein-exec lein-exec-p project.clj  
 mv lein-exec lein-exec-p ~/bin  # assuming ~/bin is in PATH  

The script

Let’s build this up in parts so it’s easy to understand.

Receiving arguments

#!/bin/bash lein-exec

(let [x (read-string (last *command-line-args*))]
        (println x))

The first line in the file tells bash to use lein-exec to run the script.
The next line reads in the *command-line-args* as a list, takes the last value in the list (our passed argument), converts it to an integer, binds it to the symbol x and prints it out to the command line.

  • Save the file: clj_tutorial.clj
  • Give it permissions: chmod a+x clj_tutorial.clj
  • Run it: ./clj_tutorial.clj 28

it should print out 28 on the command line.

Finding Factors

instead of testing every integer between 1 and the composite integer we’re testing, we can optimise the algorithm to just test all integers up to the square root of the composite integer. We can find all the remaining factors using the calcalation integer/factor

Create a range

So let’s write a function to create a list of all integers up to and including the square root of a number.

(defn getRange [x]
    (range 1 (+ x 1)))

(getRange (Math/sqrt x))

Test for factors

To test if an integer is a factor we use the modulo function, which is part of Clojure.core.

(defn isFactor [x1 x]
    (= 0 (mod x x1)))

where x is the number we’re testing and c is the composite integer.

Find all factors up to sqrt

Let’s put this together to find the factors of the integer up to the square root. We will use the list of integers we created and filter them based on the factor test.

(defn getSubFactors [x]
    (filter #(isFactor % x) (getRange (Math/sqrt x))))

Find all the factors

We now need to find the higher factors as previously explained and create a new list that contains all of the factors.

(defn getFactors [x]
    (let [factors (getSubFactors x)]
        (concat factors (getInvFactors factors x))))

Notice how we use let to bind the expression (getSubFactors x) to the symbol factors

Sum the factors

Functional programming makes a mockery out of how we would normally do a summation in an imperative programming language. In Clojure, this is how we sum the whole collection of factors

(defn sum [x]
    (reduce + x))

Reduce is taking each value of the collection x and applying the 1st argument (+) between the previous result and the current value … this does our summation for us.

Testing for perfect

Now we just have to put these methods together to see if the sum of the factors equals the original composite. In our case the composite itself appears in the list of factors, so we simply test for twice the composite value like this

(defn classify [x]
    (if (= (* 2 x) (sum (getFactors x)))
        (str x " perfect")
        (str x " not perfect")))

Final script

So here is our final script, which should tell us whether or not an integer is perfect. Test it out with the following perfect numbers 6, 28, 496, 8128

#!/bin/bash lein-exec

(defn isFactor [x1 x]
    (= 0 (mod x x1)))

(defn getRange [x]
    (range 1 (+ x 1)))

(defn getSubFactors [x]
    (filter #(isFactor % x) (getRange (Math/sqrt x))))

(defn getInvFactors [x1 x]
    (map #(/ x %) x1))

(defn getFactors [x]
    (let [factors (getSubFactors x)]
        (concat factors (getInvFactors factors x))))

(defn sum [x]
    (reduce + x))

(defn classify [x]
    (if (= (* 2 x) (sum (getFactors x)))
        (str x " perfect")
        (str x " not perfect")))

(let [x (read-string (last *command-line-args*))]
        (println (classify x)))

Sunday, November 30, 2014

FRP using RxJS and Firebase

FRP using RxJS and Firebase

FRP (Functional Reactive Programming) is the new cool thing. It allows elegant solutions without relying on complex state variable logic and allowing a pure functional approach to writing clean code that is much more error resistant.

RxJS

There are several Javascript FRP implementations available. The most common ones being BaconJS, RxJS and Kefir. Bacon seems a little bit old and slow, whereas Kefir seems very promising and fast but there isn’t much adoption and therefore little in the way of help on-line when you get stuck. RxJS has been developed by Microsoft and is seemingly the most popular (next to BaconJS).

Firebase

Firebase is an excellent cloud-based data storage and synchronisation service that provides a very nice API in various forms (JS, RESTful etc) to interact with the store. It works like a large JSON structure that you can store, query at any node … and is extremely fast and scalable.

The Application [live demo here ]

To get to know FRP a little bit I decided to write a simple multi-user Scribble program using these two libaries (plus JQuery). Using an HTML5 canvas we can set-up some event streams from the mouse interaction. First let’s create the HTML

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" >
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta charset="utf-8">
        <meta name="description" content="">
        <meta name="author" content="">
        <title>Rx for JavaScript Rocks!</title>
        <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
        <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap-theme.min.css">
        <link rel="stylesheet" href="binding.css">
    </head>
    <body>
        <div class="container">
            <div class="page-header">
                <h1>Shared Whiteboard</h1>
            </div>
            <div class="pull-left controls-group">
               <label>Colour:</label> 
               <input id="colour" value="#df4b26">
            </div>
            <button class="pull-right btn btn-primary" id="clear">
                Clear Canvas
            </button>
            <div class="row-fluid">
                <canvas id="tutorial" width="640" height="480"></canvas>
            </div>
        </div>
        <script src="bower_components/jquery/dist/jquery.min.js"></script>
        <script src="bower_components/firebase/firebase.js"></script>
        <script src="bower_components/rxjs/dist/rx.lite.compat.js"></script>
        <script src="rx.firebase.js"></script>

        <script src="main.js"></script>
    </body>
</html>

We set up a simple bootstrap view here with a canvas element, a text field for setting the scribble colour, and a button to clear the canvas.

Now to the Javascript. For those not familiar with FRP methodologies, we want to handle as much as we can as streams of data (Observables) that we perform functional processes on to do something sensible with them. Instead of triggering state changes and switching on different states, we want a continuous approach where the decision making is done by the flow of the events through our functional pipeline.

To achieve this we first have to create Observables from our mouse interactions.

var mouseDowns  = Rx.Observable.fromEvent(canvas, 'mousedown');
var mouseUps    = Rx.Observable.fromEvent(document, 'mouseup');
var mouseMoves  = Rx.Observable.fromEvent(canvas, 'mousemove');

We can model a mouse drag event by pulling mouse-move events when we get a mouse-down event, until we get a mouse-up event. In RxJS this is simply

var mouseDrags = mouseDowns.select(function (downEvent) {
    return mouseMoves.takeUntil(mouseUps).select(function (drag) {
        return functionToGetStuffWeWant(drag);
    });
});

This creates an Observable with events for each drag event, inside each drag event is an Observable of move events, which each contain some stuff we want. In our case we want to store the mouse positions, which can be retrieved from the move event that we pass to our function.

OK, so now we want to do something with the drag events containing mouse positions; we want to send them to Firebase. First we will set up a Firebase reference

var _ref = new Firebase('https://conseal.firebaseio.com/scribble');

and now we will subscribe to the mouse drag Observable and push points to the Firebase

mouseDrags.subscribe(function (drags) {
    $colour = $('#colour').val();
    $colour = $colour ? $colour : '#df4b26';
    var _dragref = _ref.push({colour: $colour});
    drags.subscribe(function (move) {
        _dragref.ref().child('points').push({x: move.x, y: move.y});
    });
});

Here we store the current colour from the view in a new line element by pushing a new object to the Firebase each time we get a drag event. Then we subscribe to the move events and push each move position that we stored in the Observable earlier to a child of the new object we just created. We then end up with something like this on Firebase

{
    "conseal": {
        "scribble": {
            "-Jc2M-VOB-iH5s7sbKvg": {
                "colour": 'black',
                "points": {
                    "-Jc2Q8N7sBLhOB3pzWzF" : {
                        "x" : 226,
                        "y" : 293
                    },
                    "-Jc2Q8Ncwp_O_3zyQXXZ" : {
                        "x" : 227,
                        "y" : 293
                    },
                    "-Jc2Q8O-9DCgdE8Xdspx" : {
                        "x" : 228,
                        "y" : 293
                    }
                }
            }
        }
    }
}

So now we want to draw our points on the canvas by responding to Firebase events and drawing the points we get from there, so that any user’s points will be retrieved and drawn.
I decided to use a simple helper library called rx.firebase.js, which allows us to treat Firebase as RxJS Observables.

We then register a subscriber that picks up when a new line is drawn, i.e. a child is added to the “scribble” location. We then want to register another subscriber to get added points. When a new point is added, we recover the parent node to get the colour and draw a line between the previous node and the current node. Doing it this way allows the code to respond to events and draw them in context without any reference to state. This means that multiple users can draw at the same time in different colours and the program will handle them all independently.

Below is the full javascript code used.

(function (window, undefined) {
    // Calculate offset either layerX/Y or offsetX/Y
    function getOffset(event) {
        return {
            x: event.offsetX === undefined ? event.layerX : event.offsetX,
            y: event.offsetY === undefined ? event.layerY : event.offsetY
        };
    }

    function main() {
        var _ref = new Firebase('https://conseal.firebaseio.com/scribble');
        var canvas = document.getElementById('tutorial');

        if (canvas.getContext) {
            var ctx = canvas.getContext('2d');
            ctx.lineWidth = 3;

            var mouseDowns  = Rx.Observable.fromEvent(canvas, 'mousedown');
            var mouseUps    = Rx.Observable.fromEvent(document, 'mouseup');
            var mouseMoves  = Rx.Observable.fromEvent(canvas, 'mousemove');
            var clearButton = Rx.Observable.fromEvent($('#clear'), 'click');

            var mouseDrags = mouseDowns.select(function (downEvent) {
                return mouseMoves.takeUntil(mouseUps).select(function (drag) {
                    return getOffset(drag);
                });
            });

            // UI EVENTS
            mouseDrags.subscribe(function (drags) {
                $colour = $('#colour').val();
                $colour = $colour ? $colour : '#df4b26';
                var _dragref = _ref.push({colour: $colour});
                drags.subscribe(function (move) {
                    _dragref.ref().child('points').push({x: move.x, y: move.y});
                });
            });

            clearButton.subscribe(function () {
                _ref.remove();
            });

            // DRAWING CODE - called from Firebase event
            var drawLine = function (data) {
                // get current point
                var coordsTo = data.snapshot.val();
                // get colour
                data.snapshot.ref().parent().parent().child('colour')
                .once('value', function (snap) {
                    var colour = snap.val();
                    // get previous point
                    data.snapshot.ref().parent().child(data.prevName)
                    .once('value', function (snap) {
                        var coordsFrom = snap.val();
                        ctx.beginPath();
                        ctx.strokeStyle = colour;
                        ctx.moveTo(coordsFrom.x, coordsFrom.y);
                        ctx.lineTo(coordsTo.x, coordsTo.y);
                        ctx.stroke();
                    });
                });
            };

            // FIRBASE EVENTS
            _ref.observe('child_added')
            .subscribe(function (newLine) {
                newLine.snapshot.child('points').ref()
                .observe('child_added')
                .filter(function (data) { return data.prevName !== null;})
                .subscribe(drawLine);
            });

            _ref.on('child_removed', function (snap) {
                canvas.width = canvas.width;
                ctx.lineWidth = 3;
            });
        }
    }

    main();
}(window));

Written with StackEdit.

Saturday, November 01, 2014

Weasel

Dawkin’s Weasel Evolution Model

Here is some Javascript to implement Dawkin’s Weasel algorithm as demonstrated in the 1987 BBC Horizon program “The Blind Watchmaker”. The algorithm is supposed to demonstrate the evolutionary process of cumulative natural selection, it is in fact simply a stochastic search algorithm with a future target .. but an interesting toy problem nonetheless.

LIVE DEMO HERE

The algorithm works as follows:

  1. select a random string of length k and make it our current string
  2. select a target string of length k
  3. generate p copies of the current string
  4. mutate the letters in each of the copies with probably m for random letters
  5. score s each copy by counting the number of correct characters that match the target
  6. choose the string with highest score to be the current string
  7. repeat steps 3 - 6 until the score s=k
    (we restrict the algorithm to only work on uppercase characters and spaces n=27)

First let’s define some helper functions and the set of characters we will be choosing strings from

/* the set of available chatacters */
var options = [" ","A","B","C","D","E","F","G","H",
               "I","J","K","L","M","N","O","P","Q",
               "R","S","T","U","V","W","X","Y","Z"];

/* function: create an array [0..i] */
var range = function (i) {
    return i?range(i-1).concat(i):[];
};

/* function: generate random number min <= r <= max */
var rand = function (min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
};

/* function: generate a random string of length k */
var getRandomString = function (k) {
    return range(k).map(function () {
        return options[rand(0,k-1)].toLowerCase();
    }).join('');
};

/* function: capitalise letter */
var capitalise = function (string, i)
{
    var start = string.slice(0,i),
        letter = string.charAt(i).toUpperCase(),
        end = string.slice(i+1);
    return start + letter + end;
};

Now we will define the evolve() function. We need to initialise some parameters including a cutoff to make sure the algorithm doesn’t carry on forever if it doesn’t converge.

var evolve = function (init, target, p, m) {
    var k = init.length, // number of letters in string
        n = options.length, // number of characters to use
        cutoff = 1000, // failsafe to stop if not converging
        iter = 0; // initiliase iteration counter
    ...    

Let’s define some functions inside of the evolve function. We want to use the closure of evolve so that we can make use of the parameters in sub-functions. We will take a “functional” approach to programming this, so will rely on functions like map(), filter(), reduce() etc.

We can now define a function called clone() that will make p copies of the initial string

/* function: create p clones of the string */
var clone = function (string, p) {
    return range(p).map(function () {
        return string;
    });
};

Similarly, we will define a mutate() function, which takes a string and mutates letters with probability m. This function will be called from a map() on the array of cloned strings.

/* function: mutate letters with probability m */
var mutate = function (string) {
    var map = Array.prototype.map;
    return map.call(string, function (letter) {
        if (Math.random() < m) {
            return options[rand(0,k-1)].toLowerCase(); // mutate
        } else {
            return letter; // don't mutate
        }
    }).join('');
};

Now our score() function will map() each mutated string and generate a score for it, replacing the original string with an object containing the string and its score.

/* function: score the string compared to the target */
var score = function (string) {
    var output,
        filter = Array.prototype.filter,
        matched = filter.call(string, function (letter, i) {
        if (letter.toUpperCase() === target[i]) {
            string = capitalise(string, i);
            return true;
        }
    });
    output = {string: string, score: matched.length};
    return output;
};

Here is a function that will select the best string (the first string with the highest score) when called from a reduce() function on our array of mutated copies.

/* function: select the best scored string */
var best = function (prev, curr) {
    if (!prev || curr.score > prev.score) {
        return curr;
    } else {
        return prev;
    }
};

So now let’s plug it all together and run it.

/* the set of available chatacters */
var options = [" ","A","B","C","D","E","F","G","H",
               "I","J","K","L","M","N","O","P","Q",
               "R","S","T","U","V","W","X","Y","Z"];

/* function: create an array [0..i] */
var range = function (i) {
    return i?range(i-1).concat(i):[];
};

/* function: generate random number min <= r <= max */
var rand = function (min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
};

/* function: generate a random string of length k */
var getRandomString = function (k) {
    return range(k).map(function () {
        return options[rand(0,k-1)].toLowerCase();
    }).join('');
};

/* function: capitalise letter */
var capitalise = function (string, i)
{
    var start = string.slice(0,i),
        letter = string.charAt(i).toUpperCase(),
        end = string.slice(i+1);
    return start + letter + end;
};

var evolve = function (init, target, p, m) {
    var k = init.length, // number of letters in string
        n = options.length, // number of characters to use
        cutoff = 1000, // failsafe to stop if not converging
        iter = 0; // initiliase iteration counter

    /* function: create p clones of the string */
    var clone = function (string, p) {
        return range(p).map(function () {
            return string;
        });
    };

    /* function: mutate letters with probability m */
    var mutate = function (string) {
        var map = Array.prototype.map;
        return map.call(string, function (letter) {
            if (Math.random() < m) {
                return options[rand(0,k-1)].toLowerCase(); // mutate
            } else {
                return letter; // don't mutate
            }
        }).join('');
    };

    /* function: score the string compared to the target */
    var score = function (string) {
        var output,
            filter = Array.prototype.filter,
            matched = filter.call(string, function (letter, i) {
            if (letter.toUpperCase() === target[i]) {
                string = capitalise(string, i);
                return true;
            }
        });
        output = {string: string, score: matched.length};
        return output;
    };

    /* function: select the best scored string */
    var best = function (prev, curr) {
        if (!prev || curr.score > prev.score) {
            return curr;
        } else {
            return prev;
        }
    };

    // initialise the current best string
    var bestString = score(init); 

    /* run this on an interval timer to slow it down */
    var timer = setInterval(function () {
        var scores = clone(bestString.string, p)
        .map(mutate)
        .map(score);

        bestString = scores.reduce(best);
        console.log(iter, bestString, running);

        if (bestString.score === k || iter === cutoff) {
            clearInterval(timer);
        }
        iter++;
    }, 100);
};

var initialString = getRandomString(28),
    targetString = "METHINKS DAWKINS GOTIT WRONG",
    p = 100,
    m = 0.04;
evolve(initialString, targetString, p, m);

You can run this code using nodeJS on the command line.

Tuesday, June 03, 2014

Famo.us Angular website with navigation

Simple website using Famo.us-Angular

In my previous tutorial I showed how you can quickly get Famo.us and AngularJS working together using Famous-Angular to create a simple array of boxes.

What shall we make?

Now let’s take it one step further and create a simple website that uses Famo.us to handle animation of a navigation pane and some simple page fade transitions.

Here it is in action as my music website Stu Kennedy

We want the animation to work whether you click on the navigation bar or on a link that should change the navigation, so the navigation bar need to respond to route changes. I decided to go with Angular’s optional ui-router module.

The source code for this can be found here on Github.

Before you start, tools you will need

  • Download and install git
  • Download and install nodeJS
  • Install bower npm install bower

Create your app:

Install bower libraries:-

bower install famous-angular
bower install angular-ui-router
bower install jquery
bower install angular-animate
bower install bootstrap

this should download the dependencies into bower_components.

Create an index.html which looks like this

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html ng-app="integrationApp">
    <head>
        <title></title>
        <link rel="stylesheet" href="bower_components/famous-angular/dist/famous-angular.min.css"/>
        <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Quicksand">
        <link rel="stylesheet" href="css/main.css"/>
    </head>
    <body>
        <script src="bower_components/jquery/dist/jquery.min.js"></script>
        <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
        <script src="bower_components/angular/angular.min.js"></script>
        <script src="bower_components/angular-animate/angular-animate.js"></script>
        <script src="bower_components/angular-ui-router/release/angular-ui-router.min.js"></script>
        <script src="bower_components/requirejs/require.js"></script>
        <script src="bower_components/famous-angular/dist/famous-angular.min.js"></script>
        <script>
            //set requirejs's base to where the famous lib folder lives
            require.config({baseUrl: 'bower_components'});
        </script>

        <script src="scripts/app.js"></script>
        <script src="scripts/controllers/navbar.js"></script>
        <script src="scripts/controllers/content.js"></script>
    </body>
</html>

Create the Angular module:

We will now create a scripts/app.js file to create the Angular app and inject famous.angular. Let’s add ui.router so that we can define all the routes for pages in our website.

angular.module('integrationApp',['famous.angular', 'ui.router', 'ngAnimate'])
.config(function($stateProvider, $urlRouterProvider){

    $urlRouterProvider.otherwise("/");
    $stateProvider
    .state("main", {
        url: "/",
        templateUrl: "views/home.html"
    })
    .state("about", {
        url: "/about",
        templateUrl: "views/about.html"
    })
    .state("projects", {
        url: "/projects",
        templateUrl: "views/projects.html"
    })
    .state("audio", {
        url: "/audio",
        templateUrl: "views/audio.html"
    })
    .state("news", {
        url: "/news",
        templateUrl: "views/news.html"
    })
    .state("contact", {
        url: "/contact",
        templateUrl: "views/contact.html"
    })
    ;
});

Create the Famo.us Template

let’s go back to index.html and create some Famo.us views to contain the various parts of our website. Inside the <body> tag put the following

<fa-app style="height: 700px; width: 1400px; margin: 0;">
    <fa-view>
        <fa-modifier fa-translate="[250,0]">
            <fa-surface fa-size="[800,50]">
                <center><h1>my website</h1></center>
            </fa-surface>
        </fa-modifier>
    </fa-view>
    <fa-view>
        <fa-modifier fa-translate="[20,80]" fa-size="[180,300]">
            <ng-include ng-controller="NavbarCtrl" src="'views/navbar.html'"></ng-include>
        </fa-modifier>
    </fa-view>
    <fa-view ng-controller="ContentCtrl">
        <fa-modifier fa-translate="[250,80]" fa-opacity="opacity.get()">
            <fa-surface fa-size="[800,600]" class="main_content">
                <ui-view></ui-view>
            </fa-surface>
        </fa-modifier>
    </fa-view>
    <fa-view>
        <fa-modifier fa-translate="[250,685]">
            <fa-surface fa-size="[800,50]" class="footer">
                © 2014 Stu Kennedy
            </fa-surface>
        </fa-modifier>
    </fa-view>
</fa-app>

Here we are creating a header, a navigation bar, a content pane and a footer.

We now need to create the navigation bar itself, which we just ng-included in-line above using <ng-include ng-controller="NavbarCtrl" src="'views/navbar.html'"> … this is where the magic happens.


Create the Navigation Bar:

The navigation bar comprises two parts; the template views/navbar.html and the controller, which we will put in ‘scripts/controllers/navbar.js’

Here is the template

<fa-view ng-repeat="box in boxes">
    <fa-modifier fa-translate="[0, (height+5)*$index, 0]">
        <fa-modifier fa-translate="box.trans.get()">
            <fa-surface fa-size="[width,height]"
                        fa-click="click($index)"
                        class="menu">
                <i class="glyphicon glyphicon-{{box.icon}}"></i>
                {{box.text}}
            </fa-surface>
        </fa-modifier>
    </fa-modifier>
</fa-view>
<fa-view>
    <fa-modifier fa-translate="cursor.get()">
        <fa-surface
                    fa-size="[10,height]"
                    class="cursor">
        </fa-surface>
    </fa-modifier>
</fa-view>

This iterates over an array on the $scope called boxes which holds a Famo.us Transitionable for each menu item.

As you can see, the array of menu items are modified according to each respective Transitionable, which we will affect from the controller. Other parameters are also set in the controller, such as the size of the boxes. We also have a cursor surface which will be moved vertically to highlight the selected item.

Here is the controller:

angular.module('integrationApp')
.controller('NavbarCtrl',function ($scope, $rootScope, $famous, $location) {
    var Transitionable  = $famous["famous/transitions/Transitionable"];
    var i;
    $scope.cursor = new Transitionable([0,0,0]);
    $scope.width = 130;
    $scope.height = 30;

    $scope.boxes = [{text: 'Home', url: '/', icon: 'home'},
                    {text: 'About', url: '/about', icon: 'user'},
                    {text: 'Projects', url: '/projects', icon: 'list'},
                    {text: 'Audio', url: '/audio', icon: 'headphones'},
                    {text: 'News', url: '/news', icon: 'warning-sign'},
                    {text: 'Contact', url: '/contact', icon: 'envelope'}];
    $scope.selected = null;

    for (i = 0; i < $scope.boxes.length; i++)
        $scope.boxes[i].trans = new Transitionable([0,0,0]);

    $scope.animate = function (index) {
        $scope.boxes[index].trans.set([30,0,50], {duration: 500, curve: 'easeOut'});

        $scope.cursor.set([0,index*($scope.height + 5),50], {duration: 500, curve: 'easeOut'});
        var pos_1 = Math.min(index,$scope.selected);
        var pos_2 = Math.max(index,$scope.selected);
        for (var i = pos_1 + 1; i < pos_2; i++) {
            $scope.boxes[i].trans.set([15,0,50], {duration: 500, curve: 'easeOut'});
            $scope.boxes[i].trans.set([0,0,50], {duration: 100, curve: 'easeOut'});
        }

        if (index != $scope.selected && $scope.selected !== null)
            $scope.boxes[$scope.selected].trans.set([0,0,0], {duration: 500, curve: 'easeOut'});
        $scope.selected = index;
    };

    $scope.click = function (index) {
        $location.path( $scope.boxes[index].url );
    };

    $rootScope.$on('$locationChangeStart', function(event) {
        $rootScope.$broadcast('transition_out');
    });

    $rootScope.$on('$locationChangeSuccess', function(event) {
        var i;
        $rootScope.$broadcast('transition_in');
        var url = $location.path();
        for (i = 0; i < $scope.boxes.length; i++) {
            if ($scope.boxes[i].url === url) {
                if($scope.selected !== i)
                    $scope.animate(i);
            }
        }
    });

});

Here, we setup the parameters for the menu items (text, url, icon) and load in some Transitionables ready to work on the respective <fa-modifier> tags in the template. The event $locationChangeSuccess is caught from ui-router and we determine from the $location.path() which box should be selected. We then animate the correct box.

The animate method deselects the previous selected box (which we store in $scope.selected) and then selected the new box. It also animates the cursor to move next to the newly selected box. Additionally if the cursor has to move over other boxes to get to the new selection we move those boxes out of the way temporarily using the for loop.


Stylesheet

We won’t be able to see anything without the stylesheet, which sets the background-color for the surfaces amongst other things, so here it is

root {
    display: block;
}

body {
    font-family: 'Quicksand', sans-serif;
    font-size: 18px;
    background-color: #231F20;
    color: #fff;
}

h1 {
    font-family: 'Quicksand', sans-serif;
    font-size: 48px;
    color: #CFCFCD;
}

h3 {
    color: #EC1D25;
}

a {
    color: #EC1D25;
}

hr {
    border-color: #444;
}

i {
    color: #CFCFCD;
}

input, textarea {
    background-color: #000;
    border-color: #444;
    border-width: 1px;
    border-style: solid;
    padding: 4px;
}

.menu {
    padding-left: 10px;
    padding-top: 3px;
    font-family: Helvetica;
    font-family: 'Quicksand', sans-serif;
    font-size: 18px;
    cursor: pointer;
    background-color: #333;
    border-color: #444;
    border-width: 1px;
    border-style: solid;
    border-radius: 4px;
    color: #EC1D25;
}

.cursor {
    background-color: #333;
    border-color: #444;
    border-width: 1px;
    border-style: solid;
}

.main_content {
    padding: 20px;
    border-style: solid;
    border-width: 1px;
    border-color: #444;
    background-color: #333;
    border-radius: 10px;
}

.footer {
    font-family: Helvetica;
    font-family: 'Quicksand', sans-serif;
    font-size: 10px;
}

Fading content pane

So we’re nearly done, we need to create some content in the views folder for our router to find. But there is one more thing … you may have noticed in our Navbar controller we $broadcast two events transition_in and transition_out. We can pick these up on our $rootScope and handle a fade out and fade in.

Let’s create scripts/controllers/content.js

angular.module('integrationApp')
.controller('ContentCtrl',function ($scope, $rootScope, $famous) {
    var Transitionable  = $famous["famous/transitions/Transitionable"];
    $scope.opacity = new Transitionable(0);
    $rootScope.$on('transition_out', function () {
        $scope.opacity.set(0);
    });

    $rootScope.$on('transition_in', function () {
        $scope.opacity.set(1, {duration: 3000, curve: 'easeOut'});
    });
});

This sets a Transitionable on the fa-opacity attribute of our content pane modifier. We then pickup the close and open events, hopefully fading out before the router changes then content and then fading in slowly.

That’s it! We’re done … just run the index.html file in your browser