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

define(function(require, exports, module) {
    var EventHandler = require('../events/EventHandler');
    var SimpleStream = require('../streams/SimpleStream');
    var ResizeStream = require('../streams/ResizeStream');
    var SizeObservable = require('../streams/SizeObservable');

    /**
     * Encapsulates a stream of size data (size, proportions, margins).
     *  Listens on start/update/end events, batches them, and emits resize events downstream
     *  to descendant size nodes.
     *
     *  Size can be defined with height and width given numerically, but
     *  they can also be:
     *
     *  ```
     *      `undefined` - takes the parent value
     *      `true`      - takes the DOM calculated value
     *      `false`     - value defined by setting an aspect ratio
     *  ```
     *
     *  @example
     *
     *      var context = Context();
     *
     *      var surface = new Surface({
     *          size : [100,100],
     *          properties : {background : 'red'}
     *      });
     *
     *      var sizeNode = new SizeNode({
     *          size : [100, undefined],
     *          margins : [50, 50]
     *      });
     *
     *      context.add(sizeNode).add(surface);
     *      context.mount(document.body)
     *
     * @class SizeNode
     * @namespace Core
     * @constructor
     * @private
     * @param sources {Object}                      Object of size sources
     * @param [sources.size] {Stream|Array}         Size source
     * @param [sources.margin] {Stream|Array}       Margin source
     * @param [sources.proportions] {Stream|Array}  Proportions source
     * @param [sources.aspectRatio] {Stream|Number} Aspect ratio source
     */
    function SizeNode(sources) {
        this.stream = _createStream(sources);
        EventHandler.setOutputHandler(this, this.stream);

        this.stream._eventInput.on('start', function(data){
            this.stream.trigger('resize', data);
        }.bind(this));

        this.stream._eventInput.on('update', function(data){
            this.stream.trigger('resize', data);
        }.bind(this));

        this.stream._eventInput.on('end', function(data){
            this.stream.trigger('resize', data);
        }.bind(this));
    }

    // Enumeration of types of size properties
    SizeNode.KEYS = {
        size : 'size',
        proportions : 'proportions',
        margins : 'margins',
        aspectRatio : 'aspectRatio'
    };

    /**
     * Introduce new data streams to the size node in {key : value} pairs.
     *  Here the `key` is one of "size", "proportions" or "marins".
     *  The `value` is either a stream, or a simple type like a `Number` or `Array`.
     *  Simple types will be wrapped in an `Observerable` to emit appropriate events.
     *
     * @method set
     * @param obj {Object}      Object of data sources
     */
    SizeNode.prototype.set = function(obj){
        // TODO: be able to overwrite streams. Not only add
        for (var key in obj){
            var value = obj[key];

            var source = (value instanceof SimpleStream)
                ? value
                : new SizeObservable(value);

            this.stream.addStream(key, source);
        }
    };

    function _createStream(sources){
        for (var key in sources){
            var value = sources[key];
            if (typeof value == 'number' || value instanceof Array){
                var source = new SizeObservable(value);
                sources[key] = source;
            }
        }
        return ResizeStream.merge(sources);
    }

    module.exports = SizeNode;
});