Show:
/* Copyright © 2015-2016 David Valdman */

define(function(require, exports, module){
    var View = require('samsara/core/View');
    var Surface = require('samsara/dom/Surface');
    var Transform = require('samsara/core/Transform');
    var SimpleStream = require('samsara/streams/SimpleStream');
    var Transitionable = require('samsara/core/Transitionable');
    var Accumulator = require('samsara/streams/Accumulator');

    var MouseInput = require('samsara/inputs/MouseInput');
    var TouchInput = require('samsara/inputs/TouchInput');
    var GenericInput = require('samsara/inputs/GenericInput');

    GenericInput.register({
        mouse : MouseInput,
        touch : TouchInput
    });

    /**
     *  A UI element that visualizes multi-touch interactions and mouse drags.
     *   On a `touchstart` or `mousedown` event, a `Surface` will animate in over the touch point
     *   This surface will follow the user's dragging behavior, and animate out on a `touchend` or `mouseup` event.
     *   Useful for taking screen captures to showcase what the user is doing while using a touch-responsive application.
     *
     *   This file comes with an associated CSS file fingers.css
     *
     *  @example
     *
     *      var fingers = new Fingers({
     *          diameter : 40,
     *          scaleIn : {duration : 400},
     *          scaleOut : {duration : 300}
     *      });
     *
     *      fingers.subscribe(context); // fingers listens to mouse/touch events from context
     *      context.add(fingers); // finger surfaces render into the context
     *
     * @class Fingers
     * @extends View
     * @namespace UI
     * @constructor
     * @param [options] {Object}                    Options
     * @param [options.diameter=40] {Number}        Diameter of finger surfaces
     * @param [options.opacityIn] {Object|Boolean}  Transition definition for opacity enter animation
     * @param [options.opacityOut] {Object|Boolean} Transition definition for opacity exit animation
     * @param [options.scaleIn] {Object|Boolean}    Transition definition for scale enter animation
     * @param [options.scaleOut] {Object|Boolean}   Transition definition for scale exit animation
     */
    var Fingers = View.extend({
        defaults : {
            diameter : 50,
            opacityIn : {duration : 100},
            opacityOut : {duration : 200},
            scaleIn : {duration : 300, curve : 'easeOut'},
            scaleOut : {duration : 200, curve : 'easeIn'}
        },
        initialize : function(){
            // dictionary whose keys are touchIds and values are objects containing
            // {stream, opacity, surface} references
            this.info = {};

            // external input
            var input = new GenericInput(['mouse', 'touch'], {track : 10});
            input.subscribe(this.input);

            // split incoming streams by touchId
            input.split(function(data){
                var id = data.touchId;
                return (this.info[id])
                    ? this.info[id].stream
                    : createStream.call(this, id);
            }.bind(this));
        }
    });

    function createStream(id){
        var stream = new SimpleStream();
        this.info[id] = {stream : stream};

        stream.on('start', handleStart.bind(this));
        stream.on('end', handleEnd.bind(this));
        return stream;
    }

    // create a surface, opacity and scale transform values. connect displacement of interaction with a transform
    function handleStart(data){
        var info = this.info;
        var id = data.touchId;
        var stream = info[id].stream;

        var pos = [data.event.pageX, data.event.pageY];

        var surface = new Surface({
            size : [this.options.diameter, this.options.diameter],
            origin : [.5, .5],
            classes : ['samsara-fingers']
        });

        var position = new Accumulator(pos);
        position.subscribe(stream.pluck('delta'));

        var opacity = new Transitionable(0);
        opacity.set(1, this.options.opacityIn);

        var scale = new Transitionable(0);
        scale.set(1, this.options.scaleIn);

        // save references for later deletion
        info[id].surface = surface;
        info[id].opacity = opacity;
        info[id].scale = scale;

        var transform = position.map(function(value){
            return Transform.translate(value);
        });

        var scaleTransform = scale.map(function(value){
            return Transform.scale([value, value]);
        });

        this.add({transform : transform, opacity : opacity})
            .add({transform : scaleTransform})
            .add(surface)
    }

    // animate out and remove the surface
    function handleEnd(data){
        var id = data.touchId;
        var info = this.info;

        var surface = info[id].surface;
        var isAnimationOver = 0;

        info[id].opacity.set(0, this.options.opacityOut, function(){
            isAnimationOver++;
            if (isAnimationOver === 2) surface.remove();
        });

        info[id].scale.set(0, this.options.scaleOut, function(){
            isAnimationOver++;
            if (isAnimationOver === 2) surface.remove();
        });

        // delete references
        delete info[id];
    }

    module.exports = Fingers;
});