<commit_before>(function (Math) {
    /*global CryptoJS:true */

    'use strict';

    // Shortcuts
    var C = CryptoJS;
    var C_LIB = C.lib;
    var WordArray = C_LIB.WordArray;
    var Hasher = C_LIB.Hasher;

    // Initialization and round constants tables
    var H = [];
    var K = [];

    // Compute constants
    (function () {
        function isPrime(n) {
            var sqrtN = Math.sqrt(n);
            for (var factor = 2; factor <= sqrtN; factor++) {
                if (n % factor === 0) {
                    return false;
                }
            }

            return true;
        }

        function getFractionalBits(n) {
            return ((n - (n | 0)) * 0x100000000) | 0;
        }

        var n = 2, nPrime = 0;
        while (nPrime < 64) {
            if (isPrime(n)) {
                if (nPrime < 8) {
                    H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
                }
                K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));

                nPrime++;
            }

            n++;
        }
    }());

    // Reusable object for expanded message
    var M = [];

    /**
     * SHA-256 hash algorithm.
     */
    var SHA256 = C.SHA256 = Hasher.extend({
        _doInit: function () {
        },

        _doReset: function () {
            this._state = H.slice(0);
        },

        _doProcessBlock: function (m) {
            // Shortcuts
            var s = this._state;
            var s0 = s[0];
            var s1 = s[1];
            var s2 = s[2];
            var s3 = s[3];
            var s4 = s[4];
            var s5 = s[5];
            var s6 = s[6];
            var s7 = s[7];
            var _s0 = s0;
            var _s1 = s1;
            var _s2 = s2;
            var _s3 = s3;
            var _s4 = s4;
            var _s5 = s5;
            var _s6 = s6;
            var _s7 = s7;

            // Rounds
            for (var round = 0; round < 64; round++) {
                // Expand message
                if (round < 16) {
                    var MRound = m[round];
                } else {
                    var gamma0x = M[round - 15];
                    var gamma1x = M[round - 2];
                    var MRound = (
                        M[round - 7] + M[round - 16] + (
                            ((gamma0x << 25) | (gamma0x >>> 7))  ^
                            ((gamma0x << 14) | (gamma0x >>> 18)) ^
                            (gamma0x >>> 3)
                        ) + (
                            ((gamma1x << 15) | (gamma1x >>> 17)) ^
                            ((gamma1x << 13) | (gamma1x >>> 19)) ^
                            (gamma1x >>> 10)
                        )
                    );
                }
                M[round] = MRound |= 0;

                // Computation
                var t1 = (
                    (((s4 << 26) | (s4 >>> 6)) ^ ((s4 << 21) | (s4 >>> 11)) ^ ((s4 << 7)  | (s4 >>> 25))) +
                    ((s4 & s5) ^ (~s4 & s6)) + s7 + MRound + K[round]
                );
                var t2 = (
                    (((s0 << 30) | (s0 >>> 2)) ^ ((s0 << 19) | (s0 >>> 13)) ^ ((s0 << 10) | (s0 >>> 22))) +
                    ((s0 & s1) ^ (s0 & s2) ^ (s1 & s2))
                );

                // Update working state
                s7 = s6;
                s6 = s5;
                s5 = s4;
                s4 = (s3 + t1) | 0;
                s3 = s2;
                s2 = s1;
                s1 = s0;
                s0 = (t1 + t2) | 0;
            }

            // Update state
            s[0] = (_s0 + s0) | 0;
            s[1] = (_s1 + s1) | 0;
            s[2] = (_s2 + s2) | 0;
            s[3] = (_s3 + s3) | 0;
            s[4] = (_s4 + s4) | 0;
            s[5] = (_s5 + s5) | 0;
            s[6] = (_s6 + s6) | 0;
            s[7] = (_s7 + s7) | 0;
        },

        _doFinalize: function () {
            // Shortcuts
            var data = this._data;
            var dataWords = data.words;
            var nBitsLeft = data.sigBytes * 8;
            var nBitsTotalLsw = this._nDataBitsLsw;
            var nBitsTotalMsw = this._nDataBitsMsw;

            // Add padding
            dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);

            var lengthStartIndex = (((nBitsLeft + 64) >>> 9) << 4) + 14;
            dataWords[lengthStartIndex] = nBitsTotalMsw;
            dataWords[lengthStartIndex + 1] = nBitsTotalLsw;

            data.sigBytes = dataWords.length * 4;

            // Hash final blocks
            this._process();

            // Return final hash
            return new WordArray(this._state);
        },

        clone: function () {
            var clone = SHA256.$super.prototype.clone.call(this);
            clone._state = clone._state.slice(0);

            return clone;
        }
    });
}(Math));
<commit_msg>Cosmetic.<commit_after>(function (Math) {
    /*global CryptoJS:true */

    'use strict';

    // Shortcuts
    var C = CryptoJS;
    var C_LIB = C.lib;
    var WordArray = C_LIB.WordArray;
    var Hasher = C_LIB.Hasher;

    // Initial state and round constants tables
    var STATE = [];
    var ROUND_CONSTANTS = [];

    // Compute constants
    (function () {
        function isPrime(n) {
            var sqrtN = Math.sqrt(n);
            for (var factor = 2; factor <= sqrtN; factor++) {
                if (n % factor === 0) {
                    return false;
                }
            }

            return true;
        }

        function getFractionalBits(n) {
            return ((n - (n | 0)) * 0x100000000) | 0;
        }

        var n = 2, nPrime = 0;
        while (nPrime < 64) {
            if (isPrime(n)) {
                if (nPrime < 8) {
                    STATE[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
                }
                ROUND_CONSTANTS[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));

                nPrime++;
            }

            n++;
        }
    }());

    // Reusable object for expanded message
    var M = [];

    /**
     * SHA-256 hash algorithm.
     */
    var SHA256 = C.SHA256 = Hasher.extend({
        _doInit: function () {
        },

        _doReset: function () {
            this._state = STATE.slice(0);
        },

        _doProcessBlock: function (m) {
            // Shortcuts
            var s = this._state;
            var s0 = s[0];
            var s1 = s[1];
            var s2 = s[2];
            var s3 = s[3];
            var s4 = s[4];
            var s5 = s[5];
            var s6 = s[6];
            var s7 = s[7];
            var _s0 = s0;
            var _s1 = s1;
            var _s2 = s2;
            var _s3 = s3;
            var _s4 = s4;
            var _s5 = s5;
            var _s6 = s6;
            var _s7 = s7;

            // Rounds
            for (var round = 0; round < 64; round++) {
                // Expand message
                if (round < 16) {
                    var MRound = m[round];
                } else {
                    var gamma0x = M[round - 15];
                    var gamma1x = M[round - 2];
                    var MRound = (
                        M[round - 7] + M[round - 16] + (
                            ((gamma0x << 25) | (gamma0x >>> 7))  ^
                            ((gamma0x << 14) | (gamma0x >>> 18)) ^
                            (gamma0x >>> 3)
                        ) + (
                            ((gamma1x << 15) | (gamma1x >>> 17)) ^
                            ((gamma1x << 13) | (gamma1x >>> 19)) ^
                            (gamma1x >>> 10)
                        )
                    );
                }
                M[round] = MRound |= 0;

                // Computation
                var t1 = (
                    (((s4 << 26) | (s4 >>> 6)) ^ ((s4 << 21) | (s4 >>> 11)) ^ ((s4 << 7)  | (s4 >>> 25))) +
                    ((s4 & s5) ^ (~s4 & s6)) + s7 + MRound + ROUND_CONSTANTS[round]
                );
                var t2 = (
                    (((s0 << 30) | (s0 >>> 2)) ^ ((s0 << 19) | (s0 >>> 13)) ^ ((s0 << 10) | (s0 >>> 22))) +
                    ((s0 & s1) ^ (s0 & s2) ^ (s1 & s2))
                );

                // Update working state
                s7 = s6;
                s6 = s5;
                s5 = s4;
                s4 = (s3 + t1) | 0;
                s3 = s2;
                s2 = s1;
                s1 = s0;
                s0 = (t1 + t2) | 0;
            }

            // Update state
            s[0] = (_s0 + s0) | 0;
            s[1] = (_s1 + s1) | 0;
            s[2] = (_s2 + s2) | 0;
            s[3] = (_s3 + s3) | 0;
            s[4] = (_s4 + s4) | 0;
            s[5] = (_s5 + s5) | 0;
            s[6] = (_s6 + s6) | 0;
            s[7] = (_s7 + s7) | 0;
        },

        _doFinalize: function () {
            // Shortcuts
            var data = this._data;
            var dataWords = data.words;
            var nBitsLeft = data.sigBytes * 8;
            var nBitsTotalLsw = this._nDataBitsLsw;
            var nBitsTotalMsw = this._nDataBitsMsw;

            // Add padding
            dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);

            var lengthStartIndex = (((nBitsLeft + 64) >>> 9) << 4) + 14;
            dataWords[lengthStartIndex] = nBitsTotalMsw;
            dataWords[lengthStartIndex + 1] = nBitsTotalLsw;

            data.sigBytes = dataWords.length * 4;

            // Hash final blocks
            this._process();

            // Return final hash
            return new WordArray(this._state);
        },

        clone: function () {
            var clone = SHA256.$super.prototype.clone.call(this);
            clone._state = clone._state.slice(0);

            return clone;
        }
    });
}(Math));
<|endoftext|>


<commit_before>/**
 * Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
 * Build: `lodash modularize underscore exports="node" -o ./underscore/`
 * Copyright 2012-2014 The Dojo Foundation <http://dojofoundation.org/>
 * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
 * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 * Available under MIT license <http://lodash.com/license>
 */
var baseFlatten = require('../internals/baseFlatten');

/**
 * Flattens a nested array (the nesting can be to any depth). If `isShallow`
 * is truey, the array will only be flattened a single level. If a callback
 * is provided each element of the array is passed through the callback before
 * flattening. The callback is bound to `thisArg` and invoked with three
 * arguments; (value, index, array).
 *
 * If a property name is provided for `callback` the created "_.pluck" style
 * callback will return the property value of the given element.
 *
 * If an object is provided for `callback` the created "_.where" style callback
 * will return `true` for elements that have the properties of the given object,
 * else `false`.
 *
 * @static
 * @memberOf _
 * @category Arrays
 * @param {Array} array The array to flatten.
 * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
 * @param {Function|Object|string} [callback=identity] The function called
 *  per iteration. If a property name or object is provided it will be used
 *  to create a "_.pluck" or "_.where" style callback, respectively.
 * @param {*} [thisArg] The `this` binding of `callback`.
 * @returns {Array} Returns a new flattened array.
 * @example
 *
 * _.flatten([1, [2], [3, [[4]]]]);
 * // => [1, 2, 3, 4];
 *
 * // using `isShallow`
 * _.flatten([1, [2], [3, [[4]]]], true);
 * // => [1, 2, 3, [[4]]];
 *
 * var characters = [
 *   { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
 *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
 * ];
 *
 * // using "_.pluck" callback shorthand
 * _.flatten(characters, 'pets');
 * // => ['hoppy', 'baby puss', 'dino']
 */
function flatten(array, isShallow) {
  return baseFlatten(array, isShallow);
}

module.exports = flatten;
<commit_msg>Ensure `flatten` works with `_.map` in the Underscore build.<commit_after>/**
 * Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
 * Build: `lodash modularize underscore exports="node" -o ./underscore/`
 * Copyright 2012-2014 The Dojo Foundation <http://dojofoundation.org/>
 * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
 * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 * Available under MIT license <http://lodash.com/license>
 */
var baseFlatten = require('../internals/baseFlatten');

/**
 * Flattens a nested array (the nesting can be to any depth). If `isShallow`
 * is truey, the array will only be flattened a single level. If a callback
 * is provided each element of the array is passed through the callback before
 * flattening. The callback is bound to `thisArg` and invoked with three
 * arguments; (value, index, array).
 *
 * If a property name is provided for `callback` the created "_.pluck" style
 * callback will return the property value of the given element.
 *
 * If an object is provided for `callback` the created "_.where" style callback
 * will return `true` for elements that have the properties of the given object,
 * else `false`.
 *
 * @static
 * @memberOf _
 * @category Arrays
 * @param {Array} array The array to flatten.
 * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
 * @param {Function|Object|string} [callback=identity] The function called
 *  per iteration. If a property name or object is provided it will be used
 *  to create a "_.pluck" or "_.where" style callback, respectively.
 * @param {*} [thisArg] The `this` binding of `callback`.
 * @returns {Array} Returns a new flattened array.
 * @example
 *
 * _.flatten([1, [2], [3, [[4]]]]);
 * // => [1, 2, 3, 4];
 *
 * // using `isShallow`
 * _.flatten([1, [2], [3, [[4]]]], true);
 * // => [1, 2, 3, [[4]]];
 *
 * var characters = [
 *   { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
 *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
 * ];
 *
 * // using "_.pluck" callback shorthand
 * _.flatten(characters, 'pets');
 * // => ['hoppy', 'baby puss', 'dino']
 */
function flatten(array, isShallow, guard) {
  var type = typeof isShallow;
  if ((type == 'number' || type == 'string') && guard && guard[isShallow] === array) {
    isShallow = false;
  }
  return baseFlatten(array, isShallow);
}

module.exports = flatten;
<|endoftext|>


<commit_before>import React from 'react'
import { Link } from 'react-router-dom'
import { Container, Row, Col, Button } from 'reactstrap'

import Item from './Item'
import DCSAnchorLink from '/imports/client/ui/components/DCSAnchorLink'

import i18n from '/imports/both/i18n/en'
import './styles.scss'

const SecondSectionI18N = i18n.Home.second_section

const {
  title,
  content
} = SecondSectionI18N

const {
  items
} = content

class SecondSection extends React.Component {
  render (props) {
    return (

        <Container>

{title}

          <Row className="items">
            {items.map((item, i) => (
              <Col key={i}>
                <Item item={item} />
              </Col>
            ))}
          </Row>
        </Container>
        {this.props.button === true ? <Button className='read-more' tag={Link} to='/about'>Read More</Button> : null}

    )
  }
}

export default SecondSection
<commit_msg>fixed broken link referencing old component filename<commit_after>import React from 'react'
import { Link } from 'react-router-dom'
import { Container, Row, Col, Button } from 'reactstrap'

import Item from './Item'

import i18n from '/imports/both/i18n/en'
import './styles.scss'

const SecondSectionI18N = i18n.Home.second_section

const {
  title,
  content
} = SecondSectionI18N

const {
  items
} = content

class SecondSection extends React.Component {
  render (props) {
    return (

        <Container>

{title}

          <Row className="items">
            {items.map((item, i) => (
              <Col key={i}>
                <Item item={item} />
              </Col>
            ))}
          </Row>
        </Container>
        {this.props.button === true ? <Button className='read-more' tag={Link} to='/about'>Read More</Button> : null}

    )
  }
}

export default SecondSection
<|endoftext|>


<commit_before>define([
    '../functional/invoke',
    '../functional/reduce',
    '../functional/isFunction',
    '../functional/noop'
], function(invoke, reduce, isFunction, noop) {
    'use strict';

    /**
     * @param callback
     * @param error
     * @param next
     * @returns {boolean}
     * @private
     */
    function continueError(callback, error, next) {
        var result = callback.onError(error, next);

        if (Stream.streamable(result)) {
            return true;
        } else {
            callback.onComplete();
        }
    }

    /**
     * @param callback
     * @param value
     * @param next
     * @returns {boolean}
     * @private
     */
    function continueValue(callback, value, next) {
        var result = callback.onValue(value);

        if (!Stream.continuable(result) || !Stream.continuable(next)) {
            callback.onComplete();
            return false;
        }

        if (Stream.streamable(next)) {
            // Pass control to next stream
            next.on(callback.onValue, callback.onError, callback.onComplete);
            return false;
        }

        return true;
    }

    /**
     * @param {Function} implementation
     * @constructor
     */
    function Stream(implementation) {
        var callbacks = []
          , init = false
          , closed = false;

        /**
         * @param {Function} onValue
         * @param {Function} onError
         * @param {Function} onComplete
         * @returns {Stream}
         */
        this.on = function on(onValue, onError, onComplete) {
            var callback = {
                onValue: isFunction(onValue) ? onValue : noop,
                onError: isFunction(onError) ? onError : noop,
                onComplete: isFunction(onComplete) ? onComplete : noop
            };

            if (closed) {
                callback.onComplete();
                return this;
            }

            callbacks.push(callback);

            // Lazy initialization
            if (!init) {
                init = true;
                implementation(sinkValue, sinkError, sinkComplete);
            }

            return this;
        };

        function sinkValue(value, next) {
            if (closed) {
                return;
            }

            callbacks = reduce(callbacks, function(callback, base) {
                try {
                    if (continueValue(callback, value, next)) {
                        base.push(callback);
                    }
                } catch (e) {
                    if (continueError(callback, e, next)) {
                        base.push(callback);
                    }
                }

                return base;
            }, []);
        }

        function sinkError(error, next) {
            if (closed) {
                return;
            }

            callbacks = reduce(callbacks, function(callback, base) {
                if (continueError(callback, error, next)) {
                    base.push(callback);
                }

                return base;
            }, []);

            // If any callback exists then stream is not closed.
            // An error was handled, so we continue
            closed = !callbacks.length;
        }

        function sinkComplete() {
            closed = true;
            invoke(callbacks, 'onComplete');
            callbacks = [];
        }
    }

    Stream.constructor = Stream;
    Stream.prototype.constructor = Stream;

    /**
     * @constant
     * @return {Number}
     */
    Stream.stop = -1;

    /**
     * @param {*} value
     * @return {Boolean}
     */
    Stream.streamable = function(value) {
        return value instanceof Stream;
    };

    /**
     * @param {*} value
     * @return {Boolean}
     */
    Stream.continuable = function(value) {
        return value !== Stream.stop;
    };

    return Stream;
});
<commit_msg>Change how stream is stopped and onComplete() is called<commit_after>define([
    '../functional/invoke',
    '../functional/reduce',
    '../functional/isFunction',
    '../functional/noop'
], function(invoke, reduce, isFunction, noop) {
    'use strict';

    /**
     * @param callback
     * @param error
     * @param next
     * @returns {boolean}
     * @private
     */
    function continueError(callback, error, next) {
        var result = callback.onError(error, next);

        if (Stream.streamable(result)) {
            return true;
        } else {
            callback.onComplete();
        }
    }

    /**
     * @param callback
     * @param value
     * @param next
     * @returns {boolean}
     * @private
     */
    function continueValue(callback, value, next) {
        var result = callback.onValue(value);

        if (!Stream.continuable(result)) {
            return false;
        }

        if (!Stream.continuable(next)) {
            callback.onComplete();
            return false;
        }

        if (Stream.streamable(next)) {
            // Pass control to next stream
            next.on(callback.onValue, callback.onError, callback.onComplete);
            return false;
        }

        return true;
    }

    /**
     * @param {Function} implementation
     * @constructor
     */
    function Stream(implementation) {
        var callbacks = []
          , init = false
          , closed = false;

        /**
         * @param {Function} onValue
         * @param {Function} onError
         * @param {Function} onComplete
         * @returns {Stream}
         */
        this.on = function on(onValue, onError, onComplete) {
            var callback = {
                onValue: isFunction(onValue) ? onValue : noop,
                onError: isFunction(onError) ? onError : noop,
                onComplete: isFunction(onComplete) ? onComplete : noop
            };

            if (closed) {
                callback.onComplete();
                return this;
            }

            callbacks.push(callback);

            // Lazy initialization
            if (!init) {
                init = true;
                implementation(sinkValue, sinkError, sinkComplete);
            }

            return this;
        };

        function sinkValue(value, next) {
            if (closed) {
                return;
            }

            callbacks = reduce(callbacks, function(callback, base) {
                try {
                    if (continueValue(callback, value, next)) {
                        base.push(callback);
                    }
                } catch (e) {
                    if (continueError(callback, e, next)) {
                        base.push(callback);
                    }
                }

                return base;
            }, []);
        }

        function sinkError(error, next) {
            if (closed) {
                return;
            }

            callbacks = reduce(callbacks, function(callback, base) {
                if (continueError(callback, error, next)) {
                    base.push(callback);
                }

                return base;
            }, []);

            // If any callback exists then stream is not closed.
            // An error was handled, so we continue
            closed = !callbacks.length;
        }

        function sinkComplete() {
            closed = true;
            invoke(callbacks, 'onComplete');
            callbacks = [];
        }
    }

    Stream.constructor = Stream;
    Stream.prototype.constructor = Stream;

    /**
     * @constant
     * @return {Number}
     */
    Stream.stop = -1;

    /**
     * @param {*} value
     * @return {Boolean}
     */
    Stream.streamable = function(value) {
        return value instanceof Stream;
    };

    /**
     * @param {*} value
     * @return {Boolean}
     */
    Stream.continuable = function(value) {
        return value !== Stream.stop;
    };

    return Stream;
});
<|endoftext|>


<commit_before>(function () {
    var defaults = {
        tooltip: {
            animate: true,
            opacity: 0.85,
            showTime: 300,
            hideTime: 500,
            distance: 5
        }
    };

    function render() {

        var clearHideTimer = function () {
            clearTimeout(this.tooltip.hideTimer);
        };

        var changeOpacity = function (opacity, delay) {
            if(this.options.tooltip.animate) {
                this.tooltipElement
                    .transition().duration(delay)
                        .style('opacity', opacity);
            } else {
                this.tooltipElement.style('opacity', opacity);
            }
        };

        var positionTooltip = function (d) {
            var plotLeft = this.options.chart.plotLeft;
            var plotWidth = this.options.chart.plotWidth;
            var plotTop = this.options.chart.plotTop;
            var plotHeight = this.options.chart.plotHeight;
            var distance = this.options.tooltip.distance;
            var width = parseFloat(this.tooltipElement.style('width'));
            var height = parseFloat(this.tooltipElement.style('height'));
            var pointX = this.xScale ? this.xScale(d.x) : d3.event.x;
            var pointY = this.yScale ? this.yScale(d.y) : d3.event.y;
            var alignedRight;

            var pos = {
                x: plotLeft + pointX - (distance + width),
                y: plotTop + pointY - (distance + height)
            };

            // Check outside plot area (left)
            if (pos.x < plotLeft) {
                pos.x = plotLeft + distance;
            }

            // Check outside plot area (right)
            if (pos.x + width > plotLeft + plotWidth) {
                pos.x -= (pos.x + width) - (plotLeft + plotWidth);
                // Don't overlap point
                pos.y = plotTop + pointY - (height + distance);
                alignedRight = true;
            }

            // Check outside the plot area (top)
            if (pos.y < plotTop) {
                pos.y = plotTop + distance;

                // Don't overlap point
                if (alignedRight && pointY >= pos.y && pointY <= pos.y + height) {
                    pos.y = pointY + plotTop + distance;
                }
            }

            // Check outside the plot area (bottom)
            if (pos.y + height > plotTop + plotHeight) {
                pos.y = Math.max(plotTop, plotTop + plotHeight - (height + distance));
            }

            return pos;
        };

        var onMouseOver = function (d) {
            show.call(this, d);
        };

        var onMouseOut = function () {
            changeOpacity.call(this, 0, this.options.tooltip.hideTime);
        };

        var getTooltipText = function (d) {
            function match() {
                var params = Array.prototype.slice.call(arguments);
                var list = params[0];
                var rest = params.slice(1);

                var response = _.map(list, function(fn) { return fn.apply(this, rest); }).concat([_.noop]);

                return _.first(_.select(response));
            }
            var options = this.options.tooltip;
            var formatters = [
                function (d) { return options.formatter ? _.partial(options.formatter, d) : null; },
                function (d) { return d.hasOwnProperty('x') ? _.partial(function (d) { return 'x: ' + d.x + '
' + 'y: ' + d.y; }, d) : null; },
                function (d) { return d.data && d.data.hasOwnProperty('x') ? _.partial(function (d) { return 'x: ' + d.x + '
' + 'y: ' + d.y; }, d.data) : null; },
                function (d) { return d.hasOwnProperty('value') ? _.partial(function (d) { return d.value; }, d) : null;  },
                function () { return function () { return 'NA'; }; }
            ];

            return match(formatters, d)();
        };

        var show = function (d) {
            clearHideTimer.call(this);

            this.tooltipElement.select('.text').html(getTooltipText.call(this, d));

            var pos = positionTooltip.call(this, d);

            this.tooltipElement
                .style('top', pos.y + 'px')
                .style('left', pos.x + 'px');

            changeOpacity.call(this, this.options.tooltip.opacity, this.options.tooltip.showTime);
        };

        this.tooltipElement = this.container
            .style('position', 'relative')
            .selectAll('.nw-tooltip').data([1]);

        this.tooltipElement
            .enter().append('div')
            .attr('class', 'nw-tooltip')
            .style('opacity', 0)
            .append('div')
                .attr('class', 'text');

        this.svg.selectAll('.tooltip-tracker')
            .on('mouseover.tooltip', onMouseOver.bind(this))
            .on('mouseout.tooltip',  onMouseOut.bind(this));
    }

    render.defaults = defaults;

    /**
    * Adds a tooltip on hover to all other visualizations in the Contour instance.
    *
    * Although not strictly required, this visualization does not appear unless there are one or more additional visualizations in this Contour instance for which to show the tooltips.
    *
    * ### Example:
    *
    *     new Contour({el: '.myChart'})
    *           .cartesian()
    *           .line([2, 4, 3, 5, 7])
    *           .tooltip()
    *           .render();
    *
    * @name tooltip(data, options)
    * @param {object|array} data Ignored!
    * @param {object} options Configuration options particular to this visualization that override the defaults.
    * @api public
    *
    */
    Contour.export('tooltip', render);

})();

<commit_msg>Changed the way the tooltip position is calculated to work better with non-cartesian charts and horizontal charts.<commit_after>(function () {
    var defaults = {
        tooltip: {
            animate: true,
            opacity: 0.85,
            showTime: 300,
            hideTime: 500,
            distance: 5
        }
    };

    function render(data, layer, options) {

        var clearHideTimer = function () {
            clearTimeout(this.tooltip.hideTimer);
        };

        var changeOpacity = function (opacity, delay) {
            if(this.options.tooltip.animate) {
                this.tooltipElement
                    .transition().duration(delay)
                        .style('opacity', opacity);
            } else {
                this.tooltipElement.style('opacity', opacity);
            }
        };

        // return the centroid x, y coordinates relative to the svg container
        var getCentroid = function (element) {
            var bbox = element.getBoundingClientRect();
            var clientRect = element.getBoundingClientRect();

            return [bbox.left + bbox.width/2, bbox.top + bbox.height/2];
        };

        function getPosition(options) {

        }

        var positionTooltip = function (d) {
            var pointOrCentroid = function () {
                return d3.event.target.tagName === 'path' ? getCentroid(d3.event.target) : d3.mouse(this.container.node());
            };
            var xScale = this.xScale;
            var yScale = this.yScale;
            var plotLeft = this.options.chart.plotLeft;
            var plotWidth = this.options.chart.plotWidth;
            var plotTop = this.options.chart.plotTop;
            var plotHeight = this.options.chart.plotHeight;
            var distance = this.options.tooltip.distance;
            var width = parseFloat(this.tooltipElement.style('width'));
            var height = parseFloat(this.tooltipElement.style('height'));
            var pointX = xScale ? xScale(d.x) : pointOrCentroid()[0];
            var pointY = yScale ? yScale(d.y) : pointOrCentroid()[1];
            var alignedRight;

            var clampPosition = function (pos) {
                // Check outside plot area (left)
                if (pos.x < plotLeft) {
                    pos.x = plotLeft + distance;
                }

                // Check outside plot area (right)
                if (pos.x + width > plotLeft + plotWidth) {
                    pos.x -= (pos.x + width) - (plotLeft + plotWidth);
                    // Don't overlap point
                    pos.y = plotTop + pointY - (height + distance);
                    alignedRight = true;
                }

                // Check outside the plot area (top)
                if (pos.y < plotTop) {
                    pos.y = plotTop + distance;

                    // Don't overlap point
                    if (alignedRight && pointY >= pos.y && pointY <= pos.y + height) {
                        pos.y = pointY + plotTop + distance;
                    }
                }

                // Check outside the plot area (bottom)
                if (pos.y + height > plotTop + plotHeight) {
                    pos.y = Math.max(plotTop, plotTop + plotHeight - (height + distance));
                }

                return pos;
            };

            var positioner = {
                'vertical': function verticalPositioner() {
                    var pos = {
                        x: plotLeft + pointX - (distance + width),
                        y: plotTop + pointY - (distance + height)
                    };

                    return clampPosition(pos);
                },

                'horizontal': function horizontalPositioner() {
                    var pos = {
                        x: plotLeft + pointY - (distance + width),
                        y: plotTop + pointX - (distance + height)
                    };

                    return clampPosition(pos);
                }
            };

            return options.chart.rotatedFrame ? positioner.horizontal() : positioner.vertical();

        };

        var onMouseOver = function (d) {
            show.call(this, d);
        };

        var onMouseOut = function () {
            changeOpacity.call(this, 0, this.options.tooltip.hideTime);
        };

        var getTooltipText = function (d) {
            function match() {
                var params = Array.prototype.slice.call(arguments);
                var list = params[0];
                var rest = params.slice(1);

                var response = _.map(list, function(fn) { return fn.apply(this, rest); }).concat([_.noop]);

                return _.first(_.select(response));
            }
            var options = this.options.tooltip;
            var formatters = [
                function (d) { return options.formatter ? _.partial(options.formatter, d) : null; },
                function (d) { return d.hasOwnProperty('x') ? _.partial(function (d) { return 'x: ' + d.x + '
' + 'y: ' + d.y; }, d) : null; },
                function (d) { return d.data && d.data.hasOwnProperty('x') ? _.partial(function (d) { return 'x: ' + d.x + '
' + 'y: ' + d.y; }, d.data) : null; },
                function (d) { return d.hasOwnProperty('value') ? _.partial(function (d) { return d.value; }, d) : null;  },
                function () { return function () { return 'NA'; }; }
            ];

            return match(formatters, d)();
        };

        var show = function (d) {
            clearHideTimer.call(this);

            this.tooltipElement.select('.text').html(getTooltipText.call(this, d));

            var pos = positionTooltip.call(this, d);

            this.tooltipElement
                .style('top', pos.y + 'px')
                .style('left', pos.x + 'px');

            changeOpacity.call(this, this.options.tooltip.opacity, this.options.tooltip.showTime);
        };

        this.tooltipElement = this.container
            .style('position', 'relative')
            .selectAll('.nw-tooltip').data([1]);

        this.tooltipElement
            .enter().append('div')
            .attr('class', 'nw-tooltip')
            .style('opacity', 0)
            .append('div')
                .attr('class', 'text');

        this.svg.selectAll('.tooltip-tracker')
            .on('mouseover.tooltip', onMouseOver.bind(this))
            .on('mouseout.tooltip',  onMouseOut.bind(this));
    }

    render.defaults = defaults;

    /**
    * Adds a tooltip on hover to all other visualizations in the Contour instance.
    *
    * Although not strictly required, this visualization does not appear unless there are one or more additional visualizations in this Contour instance for which to show the tooltips.
    *
    * ### Example:
    *
    *     new Contour({el: '.myChart'})
    *           .cartesian()
    *           .line([2, 4, 3, 5, 7])
    *           .tooltip()
    *           .render();
    *
    * @name tooltip(data, options)
    * @param {object|array} data Ignored!
    * @param {object} options Configuration options particular to this visualization that override the defaults.
    * @api public
    *
    */
    Contour.export('tooltip', render);

})();

<|endoftext|>


<commit_before>FamousFramework.module('famous:core:node', {
    behaviors: {
        '$self' : {
            '$yield': true
        }
    },
    events: {
        '$public': {
            'add-class': function($DOMElement, $payload) {
                $DOMElement.addClass($payload);
            },
            'align': function($famousNode, $payload) {
                $famousNode.setAlign($payload[0], $payload[1], $payload[2]);
            },
            'align-x': function($famousNode, $payload) {
                $famousNode.setAlign($payload, null, null);
            },
            'align-y': function($famousNode, $payload) {
                $famousNode.setAlign(null, $payload, null);
            },
            'align-z': function($famousNode, $payload) {
                $famousNode.setAlign(null, null, $payload);
            },
            'attach': function($payload, $famousNode) {
                $payload($famousNode);
            },
            'attributes': function($DOMElement, $payload) {
                for (var attributeName in $payload) {
                    $DOMElement.setAttribute(attributeName, $payload[attributeName]);
                }
            },
            'backface-visible': function($state, $payload, $dispatcher) {
                var style = $state.get('style') || {};
                style['-webkit-backface-visibility'] = ($payload) ? 'visible' : 'hidden';
                style['backface-visibility'] = ($payload) ? 'visible' : 'hidden';
                $dispatcher.trigger('style', style);
            },
            'base-color': function($mesh, $payload, $state) {
                $mesh.setBaseColor(new FamousFramework.FamousEngine.utilities.Color($payload));
                if (!$state.get('hasGeometry')) {
                    $mesh.setGeometry(new FamousFramework.FamousEngine.webglGeometries.Plane());
                    $state.set('hasGeometry', true);
                }
            },
            'box-shadow': function($state, $payload, $dispatcher) {
                var style = $state.get('style') || {};
                style['-webkit-box-shadow'] = $payload;
                style['-moz-box-shadow'] = $payload;
                style['box-shadow'] = $payload;
                $dispatcher.trigger('style', style);
            },
            'camera': function($camera, $payload) { $camera.set($payload[0], $payload[1]); },
            'content': function($DOMElement, $payload) {
                $DOMElement.setContent($payload);
            },
            'flat-shading': function($mesh, $payload) {
                $mesh.setFlatShading($payload);
            },
            'geometry': function($mesh, $payload, $state) {
                $mesh.setGeometry(new FamousFramework.FamousEngine.webglGeometries[$payload.shape]($payload.options));
                $state.set('hasGeometry', true);
            },
            'glossiness': function($mesh, $payload) {
                $mesh.setGlossiness($payload.glossiness, $payload.strength);
            },
            'id': function($DOMElement, $payload) {
                $DOMElement.setId($payload);
            },
            'light': function($famousNode, $payload) {
                var webglRenderables = FamousFramework.FamousEngine.webglRenderables;
                var Color = FamousFramework.FamousEngine.utilities.Color;

                if ($payload.type === 'point') {
                    new webglRenderables.PointLight($famousNode).setColor(new Color($payload.color));
                }
                else {
                    new webglRenderables.AmbientLight($famousNode).setColor(new Color($payload.color));
                }
            },
            'mount-point': function($famousNode, $payload) {
                $famousNode.setMountPoint($payload[0], $payload[1], $payload[2]);
            },
            'mount-point-x': function($famousNode, $payload) {
                $famousNode.setMountPoint($payload, null, null);
            },
            'mount-point-y': function($famousNode, $payload) {
                $famousNode.setMountPoint(null, $payload, null);
            },
            'mount-point-z': function($famousNode, $payload) {
                $famousNode.setMountPoint(null, null, $payload);
            },
            'normals': function($mesh, $payload) {
                $mesh.setNormals($payload);
            },
            'offset-position': function($famousNode, $payload) {
                var currentPos = $famousNode.getPosition();
                $famousNode.setPosition(
                    currentPos[0] + $payload[0] || 0,
                    currentPos[1] + $payload[1] || 0,
                    currentPos[2] + $payload[2] || 0
                );
            },
            'opacity': function($famousNode, $payload) {
                $famousNode.setOpacity($payload);
            },
            'origin': function($famousNode, $payload) {
                $famousNode.setOrigin($payload[0], $payload[1], $payload[2]);
            },
            'origin-x': function($famousNode, $payload) {
                $famousNode.setOrigin($payload, null, null);
            },
            'origin-y': function($famousNode, $payload) {
                $famousNode.setOrigin(null, $payload, null);
            },
            'origin-z': function($famousNode, $payload) {
                $famousNode.setOrigin(null, null, $payload);
            },
            'position': function($famousNode, $payload) {
                $famousNode.setPosition($payload[0], $payload[1], $payload[2]);
            },
            'position-offsets': function($mesh, $payload) {
                $mesh.setPositonOffsets($payload);
            },
            'position-x': function($famousNode, $payload) {
                $famousNode.setPosition($payload, null, null);
            },
            'position-y': function($famousNode, $payload) {
                $famousNode.setPosition(null, $payload, null);
            },
            'position-z': function($famousNode, $payload) {
                $famousNode.setPosition(null, null, $payload);
            },
            'remove-class': function($DOMElement, $payload) {
                $DOMElement.removeClass($payload);
            },
            'rotation': function($famousNode, $payload) {
                $famousNode.setRotation($payload[0], $payload[1], $payload[2], $payload[3]);
            },
            'rotation-x': function($famousNode, $payload) {
                $famousNode.setRotation($payload, null, null);
            },
            'rotation-y': function($famousNode, $payload) {
                $famousNode.setRotation(null, $payload, null);
            },
            'rotation-z': function($famousNode, $payload) {
                $famousNode.setRotation(null, null, $payload);
            },
            'scale': function($famousNode, $payload) {
                $famousNode.setScale($payload[0], $payload[1], $payload[2]);
            },
            'scale-x': function($famousNode, $payload) {
                $famousNode.setScale($payload, null, null);
            },
            'scale-y': function($famousNode, $payload) {
                $famousNode.setScale(null, $payload, null);
            },
            'scale-z': function($famousNode, $payload) {
                $famousNode.setScale(null, null, $payload);
            },
            'size': function($payload, $dispatcher) {
                var xSize = $payload[0];
                var ySize = $payload[1];
                var zSize = $payload[2];

                if (xSize === true) $dispatcher.trigger('size-true-x');
                else if (xSize !== undefined) $dispatcher.trigger('size-absolute-x', xSize);

                if (ySize === true) $dispatcher.trigger('size-true-y');
                else if (ySize !== undefined) $dispatcher.trigger('size-absolute-y', ySize);

                if (zSize === true) $dispatcher.trigger('size-true-z');
                else if (zSize !== undefined) $dispatcher.trigger('size-absolute-z', zSize);
            },
            'size-true': function($famousNode) {
                $famousNode.setSizeMode(2, 2, 2);
            },
            'size-true-x': function($famousNode) {
                $famousNode.setSizeMode(2, null, null);
            },
            'size-true-y': function($famousNode) {
                $famousNode.setSizeMode(null, 2, null);
            },
            'size-true-z': function($famousNode) {
                $famousNode.setSizeMode(null, null, 2);
            },
            'size-absolute': function($famousNode, $payload) {
                $famousNode.setSizeMode(1, 1, 1);
                $famousNode.setAbsoluteSize($payload[0], $payload[1], $payload[2]);
            },
            'size-absolute-x': function($famousNode, $payload) {
                $famousNode.setSizeMode(1, null, null);
                $famousNode.setAbsoluteSize($payload, null, null);
            },
            'size-absolute-y': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, 1, null);
                $famousNode.setAbsoluteSize(null, $payload, null);
            },
            'size-absolute-z': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, null, 1);
                $famousNode.setAbsoluteSize(null, null, $payload);
            },
            'size-differential': function($famousNode, $payload) {
                $famousNode.setSizeMode(0, 0, 0);
                $famousNode.setDifferentialSize($payload[0], $payload[1], $payload[2]);
            },
            'size-differential-x': function($famousNode, $payload) {
                $famousNode.setSizeMode(0, null, null);
                $famousNode.setDifferentialSize($payload, null, null);
            },
            'size-differential-y': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, 0, null);
                $famousNode.setDifferentialSize(null, $payload, null);
            },
            'size-differential-z': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, null, 0);
                $famousNode.setDifferentialSize(null, null, $payload);
            },
            'size-proportional': function($famousNode, $payload) {
                $famousNode.setSizeMode(0, 0, 0);
                $famousNode.setProportionalSize($payload[0], $payload[1], $payload[2]);
            },
            'size-proportional-x': function($famousNode, $payload) {
                $famousNode.setSizeMode(0, null, null);
                $famousNode.setProportionalSize($payload, null, null);
            },
            'size-proportional-y': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, 0, null);
                $famousNode.setProportionalSize(null, $payload, null);
            },
            'size-proportional-z': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, null, 0);
                $famousNode.setProportionalSize(null, null, $payload);
            },
            'style': function($DOMElement, $payload) {
                for (var styleName in $payload) {
                    $DOMElement.setProperty(styleName, $payload[styleName]);
                }
            },
            'unselectable': function($state, $payload, $dispatcher) {
                if ($payload) {
                    var style = $state.get('style') || {};
                    style['-moz-user-select'] = '-moz-none';
                    style['-khtml-user-select'] = 'none';
                    style['-webkit-user-select'] = 'none';
                    style['-o-user-select'] = 'none';
                    style['user-select'] = 'none';
                    $dispatcher.trigger('style', style);
                }
            }
        }
    },
    states: {
        'didTemplate': false,
        'initialContent': '',
        'hasGeometry': false
    }
})
.config({
    extends: [],
    imports: {}
});
<commit_msg>feat: Add ability to create a dynamic geometry<commit_after>FamousFramework.module('famous:core:node', {
    behaviors: {
        '$self' : {
            '$yield': true
        }
    },
    events: {
        '$public': {
            'add-class': function($DOMElement, $payload) {
                $DOMElement.addClass($payload);
            },
            'align': function($famousNode, $payload) {
                $famousNode.setAlign($payload[0], $payload[1], $payload[2]);
            },
            'align-x': function($famousNode, $payload) {
                $famousNode.setAlign($payload, null, null);
            },
            'align-y': function($famousNode, $payload) {
                $famousNode.setAlign(null, $payload, null);
            },
            'align-z': function($famousNode, $payload) {
                $famousNode.setAlign(null, null, $payload);
            },
            'attach': function($payload, $famousNode) {
                $payload($famousNode);
            },
            'attributes': function($DOMElement, $payload) {
                for (var attributeName in $payload) {
                    $DOMElement.setAttribute(attributeName, $payload[attributeName]);
                }
            },
            'backface-visible': function($state, $payload, $dispatcher) {
                var style = $state.get('style') || {};
                style['-webkit-backface-visibility'] = ($payload) ? 'visible' : 'hidden';
                style['backface-visibility'] = ($payload) ? 'visible' : 'hidden';
                $dispatcher.trigger('style', style);
            },
            'base-color': function($mesh, $payload, $state) {
                $mesh.setBaseColor(new FamousFramework.FamousEngine.utilities.Color($payload));
                if (!$state.get('hasGeometry')) {
                    $mesh.setGeometry(new FamousFramework.FamousEngine.webglGeometries.Plane());
                    $state.set('hasGeometry', true);
                }
            },
            'box-shadow': function($state, $payload, $dispatcher) {
                var style = $state.get('style') || {};
                style['-webkit-box-shadow'] = $payload;
                style['-moz-box-shadow'] = $payload;
                style['box-shadow'] = $payload;
                $dispatcher.trigger('style', style);
            },
            'camera': function($camera, $payload) { $camera.set($payload[0], $payload[1]); },
            'content': function($DOMElement, $payload) {
                $DOMElement.setContent($payload);
            },
            'flat-shading': function($mesh, $payload) {
                $mesh.setFlatShading($payload);
            },
            'geometry': function($mesh, $payload, $state) {
                var webglGeometries = FamousFramework.FamousEngine.webglGeometries;
                var geometry;

                if ($payload.dynamic) {
                    var geometry = new webglGeometries.DynamicGeometry()
                        .fromGeometry(new webglGeometries[$payload.shape]($payload.options));
                }
                else {
                    var geometry = new webglGeometries[$payload.shape]($payload.options);
                }

                $mesh.setGeometry(geometry);
                $state.set('hasGeometry', true);
            },
            'glossiness': function($mesh, $payload) {
                $mesh.setGlossiness($payload.glossiness, $payload.strength);
            },
            'id': function($DOMElement, $payload) {
                $DOMElement.setId($payload);
            },
            'light': function($famousNode, $payload) {
                var webglRenderables = FamousFramework.FamousEngine.webglRenderables;
                var Color = FamousFramework.FamousEngine.utilities.Color;

                if ($payload.type === 'point') {
                    new webglRenderables.PointLight($famousNode).setColor(new Color($payload.color));
                }
                else {
                    new webglRenderables.AmbientLight($famousNode).setColor(new Color($payload.color));
                }
            },
            'mount-point': function($famousNode, $payload) {
                $famousNode.setMountPoint($payload[0], $payload[1], $payload[2]);
            },
            'mount-point-x': function($famousNode, $payload) {
                $famousNode.setMountPoint($payload, null, null);
            },
            'mount-point-y': function($famousNode, $payload) {
                $famousNode.setMountPoint(null, $payload, null);
            },
            'mount-point-z': function($famousNode, $payload) {
                $famousNode.setMountPoint(null, null, $payload);
            },
            'normals': function($mesh, $payload) {
                $mesh.setNormals($payload);
            },
            'offset-position': function($famousNode, $payload) {
                var currentPos = $famousNode.getPosition();
                $famousNode.setPosition(
                    currentPos[0] + $payload[0] || 0,
                    currentPos[1] + $payload[1] || 0,
                    currentPos[2] + $payload[2] || 0
                );
            },
            'opacity': function($famousNode, $payload) {
                $famousNode.setOpacity($payload);
            },
            'origin': function($famousNode, $payload) {
                $famousNode.setOrigin($payload[0], $payload[1], $payload[2]);
            },
            'origin-x': function($famousNode, $payload) {
                $famousNode.setOrigin($payload, null, null);
            },
            'origin-y': function($famousNode, $payload) {
                $famousNode.setOrigin(null, $payload, null);
            },
            'origin-z': function($famousNode, $payload) {
                $famousNode.setOrigin(null, null, $payload);
            },
            'position': function($famousNode, $payload) {
                $famousNode.setPosition($payload[0], $payload[1], $payload[2]);
            },
            'position-offsets': function($mesh, $payload) {
                $mesh.setPositonOffsets($payload);
            },
            'position-x': function($famousNode, $payload) {
                $famousNode.setPosition($payload, null, null);
            },
            'position-y': function($famousNode, $payload) {
                $famousNode.setPosition(null, $payload, null);
            },
            'position-z': function($famousNode, $payload) {
                $famousNode.setPosition(null, null, $payload);
            },
            'remove-class': function($DOMElement, $payload) {
                $DOMElement.removeClass($payload);
            },
            'rotation': function($famousNode, $payload) {
                $famousNode.setRotation($payload[0], $payload[1], $payload[2], $payload[3]);
            },
            'rotation-x': function($famousNode, $payload) {
                $famousNode.setRotation($payload, null, null);
            },
            'rotation-y': function($famousNode, $payload) {
                $famousNode.setRotation(null, $payload, null);
            },
            'rotation-z': function($famousNode, $payload) {
                $famousNode.setRotation(null, null, $payload);
            },
            'scale': function($famousNode, $payload) {
                $famousNode.setScale($payload[0], $payload[1], $payload[2]);
            },
            'scale-x': function($famousNode, $payload) {
                $famousNode.setScale($payload, null, null);
            },
            'scale-y': function($famousNode, $payload) {
                $famousNode.setScale(null, $payload, null);
            },
            'scale-z': function($famousNode, $payload) {
                $famousNode.setScale(null, null, $payload);
            },
            'size': function($payload, $dispatcher) {
                var xSize = $payload[0];
                var ySize = $payload[1];
                var zSize = $payload[2];

                if (xSize === true) $dispatcher.trigger('size-true-x');
                else if (xSize !== undefined) $dispatcher.trigger('size-absolute-x', xSize);

                if (ySize === true) $dispatcher.trigger('size-true-y');
                else if (ySize !== undefined) $dispatcher.trigger('size-absolute-y', ySize);

                if (zSize === true) $dispatcher.trigger('size-true-z');
                else if (zSize !== undefined) $dispatcher.trigger('size-absolute-z', zSize);
            },
            'size-true': function($famousNode) {
                $famousNode.setSizeMode(2, 2, 2);
            },
            'size-true-x': function($famousNode) {
                $famousNode.setSizeMode(2, null, null);
            },
            'size-true-y': function($famousNode) {
                $famousNode.setSizeMode(null, 2, null);
            },
            'size-true-z': function($famousNode) {
                $famousNode.setSizeMode(null, null, 2);
            },
            'size-absolute': function($famousNode, $payload) {
                $famousNode.setSizeMode(1, 1, 1);
                $famousNode.setAbsoluteSize($payload[0], $payload[1], $payload[2]);
            },
            'size-absolute-x': function($famousNode, $payload) {
                $famousNode.setSizeMode(1, null, null);
                $famousNode.setAbsoluteSize($payload, null, null);
            },
            'size-absolute-y': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, 1, null);
                $famousNode.setAbsoluteSize(null, $payload, null);
            },
            'size-absolute-z': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, null, 1);
                $famousNode.setAbsoluteSize(null, null, $payload);
            },
            'size-differential': function($famousNode, $payload) {
                $famousNode.setSizeMode(0, 0, 0);
                $famousNode.setDifferentialSize($payload[0], $payload[1], $payload[2]);
            },
            'size-differential-x': function($famousNode, $payload) {
                $famousNode.setSizeMode(0, null, null);
                $famousNode.setDifferentialSize($payload, null, null);
            },
            'size-differential-y': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, 0, null);
                $famousNode.setDifferentialSize(null, $payload, null);
            },
            'size-differential-z': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, null, 0);
                $famousNode.setDifferentialSize(null, null, $payload);
            },
            'size-proportional': function($famousNode, $payload) {
                $famousNode.setSizeMode(0, 0, 0);
                $famousNode.setProportionalSize($payload[0], $payload[1], $payload[2]);
            },
            'size-proportional-x': function($famousNode, $payload) {
                $famousNode.setSizeMode(0, null, null);
                $famousNode.setProportionalSize($payload, null, null);
            },
            'size-proportional-y': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, 0, null);
                $famousNode.setProportionalSize(null, $payload, null);
            },
            'size-proportional-z': function($famousNode, $payload) {
                $famousNode.setSizeMode(null, null, 0);
                $famousNode.setProportionalSize(null, null, $payload);
            },
            'style': function($DOMElement, $payload) {
                for (var styleName in $payload) {
                    $DOMElement.setProperty(styleName, $payload[styleName]);
                }
            },
            'unselectable': function($state, $payload, $dispatcher) {
                if ($payload) {
                    var style = $state.get('style') || {};
                    style['-moz-user-select'] = '-moz-none';
                    style['-khtml-user-select'] = 'none';
                    style['-webkit-user-select'] = 'none';
                    style['-o-user-select'] = 'none';
                    style['user-select'] = 'none';
                    $dispatcher.trigger('style', style);
                }
            }
        }
    },
    states: {
        'didTemplate': false,
        'initialContent': '',
        'hasGeometry': false
    }
})
.config({
    extends: [],
    imports: {}
});
<|endoftext|>


<commit_before>import Controller, {inject as controller} from '@ember/controller';
import EmberObject, {action, defineProperty} from '@ember/object';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import moment from 'moment';
import {alias} from '@ember/object/computed';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {tracked} from '@glimmer/tracking';

const SCRATCH_PROPS = ['name', 'email', 'note'];

export default class MemberController extends Controller {
    @controller members;
    @service session;
    @service dropdown;
    @service membersStats;
    @service notifications;
    @service router;
    @service store;

    @tracked isLoading = false;
    @tracked showDeleteMemberModal = false;
    @tracked showImpersonateMemberModal = false;
    @tracked showUnsavedChangesModal = false;

    leaveScreenTransition = null;

    // Computed properties -----------------------------------------------------

    @alias('model') member;

    get scratchMember() {
        let scratchMember = EmberObject.create({member: this.member});
        SCRATCH_PROPS.forEach(prop => defineProperty(scratchMember, prop, boundOneWay(`member.${prop}`)));
        return scratchMember;
    }

    get subscribedAt() {
        // member can be a proxy object in a sparse array so .get is required
        let memberSince = moment(this.member.get('createdAtUTC')).from(moment());
        let createdDate = moment(this.member.get('createdAtUTC')).format('MMM DD, YYYY');
        return `${createdDate} (${memberSince})`;
    }

    // Actions -----------------------------------------------------------------

    @action
    setProperty(propKey, value) {
        this._saveMemberProperty(propKey, value);
    }

    @action
    toggleDeleteMemberModal() {
        this.showDeleteMemberModal = !this.showDeleteMemberModal;
    }

    @action
    toggleImpersonateMemberModal() {
        this.showImpersonateMemberModal = !this.showImpersonateMemberModal;
    }

    @action
    save() {
        return this.saveTask.perform();
    }

    @action
    deleteMember() {
        return this.member.destroyRecord().then(() => {
            this.membersStats.invalidate();
            return this.transitionToRoute('members');
        }, (error) => {
            return this.notifications.showAPIError(error, {key: 'member.delete'});
        });
    }

    @action
    toggleUnsavedChangesModal(transition) {
        let leaveTransition = this.leaveScreenTransition;

        if (!transition && this.showUnsavedChangesModal) {
            this.leaveScreenTransition = null;
            this.showUnsavedChangesModal = false;
            return;
        }

        if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
            this.leaveScreenTransition = transition;

            // if a save is running, wait for it to finish then transition
            if (this.save.isRunning) {
                return this.save.last.then(() => {
                    transition.retry();
                });
            }

            // we genuinely have unsaved data, show the modal
            this.showUnsavedChangesModal = true;
        }
    }

    @action
    leaveScreen() {
        this.member.rollbackAttributes();
        return this.leaveScreenTransition.retry();
    }

    // Tasks -------------------------------------------------------------------

    @task({drop: true})
    *saveTask() {
        let {member, scratchMember} = this;

        // if Cmd+S is pressed before the field loses focus make sure we're
        // saving the intended property values
        let scratchProps = scratchMember.getProperties(SCRATCH_PROPS);
        member.setProperties(scratchProps);

        if (!member.isNew) {
            this.membersStats.invalidate();
        }

        try {
            yield member.save();
            member.updateLabels();
            // replace 'member.new' route with 'member' route
            this.replaceRoute('member', member);

            return member;
        } catch (error) {
            if (error) {
                this.notifications.showAPIError(error, {key: 'member.save'});
            }
        }
    }

    @task
    *fetchMemberTask(memberId) {
        this.isLoading = true;

        this.member = yield this.store.findRecord('member', memberId, {
            reload: true
        });

        this.isLoading = false;
    }

    // Private -----------------------------------------------------------------

    _saveMemberProperty(propKey, newValue) {
        let currentValue = this.member.get(propKey);

        if (newValue) {
            newValue = newValue.trim();
        }

        // avoid modifying empty values and triggering inadvertant unsaved changes modals
        if (newValue !== false && !newValue && !currentValue) {
            return;
        }

        this.member.set(propKey, newValue);
    }
}
<commit_msg>🐛 Fixed members list not updating after deleting a member<commit_after>import Controller, {inject as controller} from '@ember/controller';
import EmberObject, {action, defineProperty} from '@ember/object';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import moment from 'moment';
import {alias} from '@ember/object/computed';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {tracked} from '@glimmer/tracking';

const SCRATCH_PROPS = ['name', 'email', 'note'];

export default class MemberController extends Controller {
    @controller members;
    @service session;
    @service dropdown;
    @service membersStats;
    @service notifications;
    @service router;
    @service store;

    @tracked isLoading = false;
    @tracked showDeleteMemberModal = false;
    @tracked showImpersonateMemberModal = false;
    @tracked showUnsavedChangesModal = false;

    leaveScreenTransition = null;

    // Computed properties -----------------------------------------------------

    @alias('model') member;

    get scratchMember() {
        let scratchMember = EmberObject.create({member: this.member});
        SCRATCH_PROPS.forEach(prop => defineProperty(scratchMember, prop, boundOneWay(`member.${prop}`)));
        return scratchMember;
    }

    get subscribedAt() {
        // member can be a proxy object in a sparse array so .get is required
        let memberSince = moment(this.member.get('createdAtUTC')).from(moment());
        let createdDate = moment(this.member.get('createdAtUTC')).format('MMM DD, YYYY');
        return `${createdDate} (${memberSince})`;
    }

    // Actions -----------------------------------------------------------------

    @action
    setProperty(propKey, value) {
        this._saveMemberProperty(propKey, value);
    }

    @action
    toggleDeleteMemberModal() {
        this.showDeleteMemberModal = !this.showDeleteMemberModal;
    }

    @action
    toggleImpersonateMemberModal() {
        this.showImpersonateMemberModal = !this.showImpersonateMemberModal;
    }

    @action
    save() {
        return this.saveTask.perform();
    }

    @action
    deleteMember() {
        return this.member.destroyRecord().then(() => {
            this.members.refreshData();
            return this.transitionToRoute('members');
        }, (error) => {
            return this.notifications.showAPIError(error, {key: 'member.delete'});
        });
    }

    @action
    toggleUnsavedChangesModal(transition) {
        let leaveTransition = this.leaveScreenTransition;

        if (!transition && this.showUnsavedChangesModal) {
            this.leaveScreenTransition = null;
            this.showUnsavedChangesModal = false;
            return;
        }

        if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
            this.leaveScreenTransition = transition;

            // if a save is running, wait for it to finish then transition
            if (this.save.isRunning) {
                return this.save.last.then(() => {
                    transition.retry();
                });
            }

            // we genuinely have unsaved data, show the modal
            this.showUnsavedChangesModal = true;
        }
    }

    @action
    leaveScreen() {
        this.member.rollbackAttributes();
        return this.leaveScreenTransition.retry();
    }

    // Tasks -------------------------------------------------------------------

    @task({drop: true})
    *saveTask() {
        let {member, scratchMember} = this;

        // if Cmd+S is pressed before the field loses focus make sure we're
        // saving the intended property values
        let scratchProps = scratchMember.getProperties(SCRATCH_PROPS);
        member.setProperties(scratchProps);

        if (!member.isNew) {
            this.membersStats.invalidate();
        }

        try {
            yield member.save();
            member.updateLabels();
            // replace 'member.new' route with 'member' route
            this.replaceRoute('member', member);

            return member;
        } catch (error) {
            if (error) {
                this.notifications.showAPIError(error, {key: 'member.save'});
            }
        }
    }

    @task
    *fetchMemberTask(memberId) {
        this.isLoading = true;

        this.member = yield this.store.findRecord('member', memberId, {
            reload: true
        });

        this.isLoading = false;
    }

    // Private -----------------------------------------------------------------

    _saveMemberProperty(propKey, newValue) {
        let currentValue = this.member.get(propKey);

        if (newValue) {
            newValue = newValue.trim();
        }

        // avoid modifying empty values and triggering inadvertant unsaved changes modals
        if (newValue !== false && !newValue && !currentValue) {
            return;
        }

        this.member.set(propKey, newValue);
    }
}
<|endoftext|>


<commit_before>import {parseGameType} from 'app/services/GameTypes';
import moment from 'moment-timezone';
import he from 'he';
import _ from 'lodash';

const IP_REGEX = /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:\d{1,5})?/g;
const DOMAIN_REGEX = /[^\w](IP|Address).*?([a-z\d]([a-z\d\-]{0,61}[a-z\d])?(\.[a-z\d]([a-z\d\-]{0,61}[a-z\d])?)+(:\d{1,5})?)/gi; // jscs:ignore maximumLineLength
const SIZE_REGEX = /To([\dX]+)/i;
const EXTRAS_REGEX = /(\[.*?])/g;

class MatchPostParser {
    constructor(DateTime, $location, $log) {
        this.DateTime = DateTime;
        this.$location = $location;
        this.$log = $log;
    }

    _parseTitle(title) {
        let details = {
            title,
            extras: []
        };

        let pipeIndex = title.indexOf('|');
        details.isStartTime = pipeIndex !== -1;

        // Replace the | with a -
        if (details.isStartTime) {
            title = title.substr(0, pipeIndex) + '-' + title.substr(pipeIndex + 1);
        }

        let sections = title
            .split('-')
            .map(part => part.trim());

        // Post isnt formatted correctly, don't display it at all
        if (sections.length < 3) {
            throw new Error('Not enough parts in post title');
        }

        let [dateRegionSection, titleSection, teamStyleSection, scenariosSection] = sections;

        // Grab the opening time and region from the first section
        _.merge(details, this._parseDateAndRegion(dateRegionSection));

        // Copy the title across without the other sections
        details.title = titleSection;

        // Assume vanilla if no gamemodes section
        if (_.isUndefined(scenariosSection)) {
            details.gamemodes = ['Vanilla'];

            // Cut out the extras from team types
            let extrasInfo = this._parseExtras(teamStyleSection);
            details.extras = extrasInfo.extras;
            teamStyleSection = extrasInfo.section;
        } else {
            // Add gamemodes
            details.gamemodes = this._parseScenarios(scenariosSection);

            // Cut out the extras from the final scenario

            if (details.gamemodes.length > 0) {
                let finalIndex = details.gamemodes.length - 1;

                let extrasInfo = this._parseExtras(details.gamemodes[finalIndex]);
                details.extras = extrasInfo.extras;
                details.gamemodes[finalIndex] = extrasInfo.section;
            }
        }

        let teamInfo = this._parseTeamStyle(teamStyleSection);
        details.teams = teamInfo.type;
        details.teamSize = teamInfo.size;

        return details;
    }

    _parseTeamStyle(section) {
        section = section.trim();

        SIZE_REGEX.exec('');
        let sizeCheck = SIZE_REGEX.exec(section);

        let size = 0;
        let style = section;

        // Check for a teamsize if we can
        if (!_.isNull(sizeCheck)) {
            size = sizeCheck[1];
            style = section.replace(SIZE_REGEX, '').trim();
        }

        // Attempt to get the game type
        let type = parseGameType(style);

        return {
            type: _.isUndefined(type) ? section : type.name,
            size: size
        };
    }

    _parseExtras(section) {
        let extras = [];
        section = section
            .replace(EXTRAS_REGEX, part => {
                extras.push(part);
                return '';
            })
            .trim();

        return { section, extras };
    }

    _parseDateAndRegion(section) {
        // Definitely not formatted correctly
        if (section.length < 3) {
            throw new Error('Incorrect formatting in date and region section');
        }

        // Parse the region as the last 2 characters
        let region = section.slice(-2).toUpperCase();

        // Attempt to parse the date from the remains
        let opens = moment.utc(section.slice(0, -2), 'MMM DD HH:mm', 'en');

        return { region, opens };
    }

    _parseScenarios(section) {
        return section.split(',').map(mode => mode.trim());
    }

    _getAddress(content) {
        // Reset regex in case of exact copy of previous post
        IP_REGEX.exec('');
        let ipcheck = IP_REGEX.exec(content);

        // Check IP before falling back to domain
        if (!_.isNull(ipcheck)) {
            let address = ipcheck[1];

            // Only show port if it's not the default port
            if (!_.isEmpty(ipcheck[2]) && ipcheck[2] !== ':25565') {
                address += ipcheck[2];
            }

            return address;
        }

        DOMAIN_REGEX.exec('');
        let domainCheck = DOMAIN_REGEX.exec(content);

        if (!_.isNull(domainCheck)) {
            return domainCheck[2];
        }
    }

    /**
     * @param element the raw post element from the JSON api
     */
    parse(element) {
        // Add basic stuff from the post itself
        // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
        let post = {
            id: element.id,
            title: element.title,
            selftext: element.selftext,
            author: element.author,
            permalink: `https://reddit.com${element.permalink}`,
            posted: moment(element.created_utc, 'X'),
            anchorlink: `#${this.$location.path()}?post=${element.id}`,
            isCommunityGame: element.link_flair_text && element.link_flair_text === 'Community Game',
            subreddit: element.subreddit,
            valid: false
        };
        // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

        // Grab all of the information from the title
        try {
            let parsed = this._parseTitle(element.title);

            // Fix html encoded entities breaking in post titles
            parsed.title = he.decode(parsed.title, {
                isAttributeValue: true
            });

            // Append the extras to the title
            if (parsed.extras.length > 0) {
                parsed.title = parsed.title + ' ' + parsed.extras.join(' ');
            }

            delete parsed.extras;
            _.merge(post, parsed);
        } catch (err) {
            this.$log.warn('Unable to parse post', element.title, post.permalink, err);
            return post;
        }

        post.address = this._getAddress(element.selftext);

        if (_.isNull(post.opens) || !post.opens.isValid()) {
            return post;
        }

        let current = this.DateTime.getTime();
        let monthsAgo = post.opens.diff(current, 'months');

        // If it's more than 6 months old, assume it's in the next year
        if (monthsAgo < -6) {
            post.opens.add(1, 'years');
        }
        // If it's more than 6 months in the future assume it's in the last year
        else if (monthsAgo > 6) {
            post.opens.subtract(1, 'years');
        }

        post.valid = true;
        return post;
    }
}
MatchPostParser.$inject = ['DateTime', '$location', '$log'];

export default MatchPostParser;
<commit_msg>fix(MatchPostParser): fix html entities in selftext + title<commit_after>import {parseGameType} from 'app/services/GameTypes';
import moment from 'moment-timezone';
import he from 'he';
import _ from 'lodash';

const IP_REGEX = /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:\d{1,5})?/g;
const DOMAIN_REGEX = /[^\w](IP|Address).*?([a-z\d]([a-z\d\-]{0,61}[a-z\d])?(\.[a-z\d]([a-z\d\-]{0,61}[a-z\d])?)+(:\d{1,5})?)/gi; // jscs:ignore maximumLineLength
const SIZE_REGEX = /To([\dX]+)/i;
const EXTRAS_REGEX = /(\[.*?])/g;

class MatchPostParser {
    constructor(DateTime, $location, $log) {
        this.DateTime = DateTime;
        this.$location = $location;
        this.$log = $log;
    }

    _parseTitle(title) {
        let details = {
            title,
            extras: []
        };

        let pipeIndex = title.indexOf('|');
        details.isStartTime = pipeIndex !== -1;

        // Replace the | with a -
        if (details.isStartTime) {
            title = title.substr(0, pipeIndex) + '-' + title.substr(pipeIndex + 1);
        }

        let sections = title
            .split('-')
            .map(part => part.trim());

        // Post isnt formatted correctly, don't display it at all
        if (sections.length < 3) {
            throw new Error('Not enough parts in post title');
        }

        let [dateRegionSection, titleSection, teamStyleSection, scenariosSection] = sections;

        // Grab the opening time and region from the first section
        _.merge(details, this._parseDateAndRegion(dateRegionSection));

        // Copy the title across without the other sections
        details.title = titleSection;

        // Assume vanilla if no gamemodes section
        if (_.isUndefined(scenariosSection)) {
            details.gamemodes = ['Vanilla'];

            // Cut out the extras from team types
            let extrasInfo = this._parseExtras(teamStyleSection);
            details.extras = extrasInfo.extras;
            teamStyleSection = extrasInfo.section;
        } else {
            // Add gamemodes
            details.gamemodes = this._parseScenarios(scenariosSection);

            // Cut out the extras from the final scenario

            if (details.gamemodes.length > 0) {
                let finalIndex = details.gamemodes.length - 1;

                let extrasInfo = this._parseExtras(details.gamemodes[finalIndex]);
                details.extras = extrasInfo.extras;
                details.gamemodes[finalIndex] = extrasInfo.section;
            }
        }

        let teamInfo = this._parseTeamStyle(teamStyleSection);
        details.teams = teamInfo.type;
        details.teamSize = teamInfo.size;

        return details;
    }

    _parseTeamStyle(section) {
        section = section.trim();

        SIZE_REGEX.exec('');
        let sizeCheck = SIZE_REGEX.exec(section);

        let size = 0;
        let style = section;

        // Check for a teamsize if we can
        if (!_.isNull(sizeCheck)) {
            size = sizeCheck[1];
            style = section.replace(SIZE_REGEX, '').trim();
        }

        // Attempt to get the game type
        let type = parseGameType(style);

        return {
            type: _.isUndefined(type) ? section : type.name,
            size: size
        };
    }

    _parseExtras(section) {
        let extras = [];
        section = section
            .replace(EXTRAS_REGEX, part => {
                extras.push(part);
                return '';
            })
            .trim();

        return { section, extras };
    }

    _parseDateAndRegion(section) {
        // Definitely not formatted correctly
        if (section.length < 3) {
            throw new Error('Incorrect formatting in date and region section');
        }

        // Parse the region as the last 2 characters
        let region = section.slice(-2).toUpperCase();

        // Attempt to parse the date from the remains
        let opens = moment.utc(section.slice(0, -2), 'MMM DD HH:mm', 'en');

        return { region, opens };
    }

    _parseScenarios(section) {
        return section.split(',').map(mode => mode.trim());
    }

    _getAddress(content) {
        // Reset regex in case of exact copy of previous post
        IP_REGEX.exec('');
        let ipcheck = IP_REGEX.exec(content);

        // Check IP before falling back to domain
        if (!_.isNull(ipcheck)) {
            let address = ipcheck[1];

            // Only show port if it's not the default port
            if (!_.isEmpty(ipcheck[2]) && ipcheck[2] !== ':25565') {
                address += ipcheck[2];
            }

            return address;
        }

        DOMAIN_REGEX.exec('');
        let domainCheck = DOMAIN_REGEX.exec(content);

        if (!_.isNull(domainCheck)) {
            return domainCheck[2];
        }
    }

    /**
     * @param element the raw post element from the JSON api
     */
    parse(element) {
        // Add basic stuff from the post itself
        // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
        let post = {
            id: element.id,
            title: element.title,
            selftext: he.decode(element.selftext),
            author: element.author,
            permalink: `https://reddit.com${element.permalink}`,
            posted: moment(element.created_utc, 'X'),
            anchorlink: `#${this.$location.path()}?post=${element.id}`,
            isCommunityGame: element.link_flair_text && element.link_flair_text === 'Community Game',
            subreddit: element.subreddit,
            valid: false
        };
        // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

        // Grab all of the information from the title
        try {
            let parsed = this._parseTitle(element.title);

            // Append the extras to the title
            if (parsed.extras.length > 0) {
                parsed.title = parsed.title + ' ' + parsed.extras.join(' ');
            }

            // Fix html encoded entities breaking in post titles
            parsed.title = he.decode(parsed.title);

            delete parsed.extras;
            _.merge(post, parsed);
        } catch (err) {
            this.$log.warn('Unable to parse post', element.title, post.permalink, err);
            return post;
        }

        post.address = this._getAddress(element.selftext);

        if (_.isNull(post.opens) || !post.opens.isValid()) {
            return post;
        }

        let current = this.DateTime.getTime();
        let monthsAgo = post.opens.diff(current, 'months');

        // If it's more than 6 months old, assume it's in the next year
        if (monthsAgo < -6) {
            post.opens.add(1, 'years');
        }
        // If it's more than 6 months in the future assume it's in the last year
        else if (monthsAgo > 6) {
            post.opens.subtract(1, 'years');
        }

        post.valid = true;
        return post;
    }
}
MatchPostParser.$inject = ['DateTime', '$location', '$log'];

export default MatchPostParser;
<|endoftext|>