<commit_before>/*!
 * numeral.js
 * version : 1.5.3
 * author : Adam Draper
 * license : MIT
 * http://adamwdraper.github.com/Numeral-js/
 */

(function () {

    /************************************
        Constants
    ************************************/

    var numeral,
        VERSION = '1.5.3',
        // internal storage for language config files
        languages = {},
        currentLanguage = 'en',
        zeroFormat = null,
        defaultFormat = '0,0',
        // check for nodeJS
        hasModule = (typeof module !== 'undefined' && module.exports);

    /************************************
        Constructors
    ************************************/

    // Numeral prototype object
    function Numeral (number) {
        this._value = number;
    }

    /**
     * Implementation of toFixed() that treats floats more like decimals
     *
     * Fixes binary rounding issues (eg. (0.615).toFixed(2) === '0.61') that present
     * problems for accounting- and finance-related software.
     */
    function toFixed (value, precision, roundingFunction, optionals) {
        var power = Math.pow(10, precision),
            optionalsRegExp,
            output;

        //roundingFunction = (roundingFunction !== undefined ? roundingFunction : Math.round);
        // Multiply up by precision, round accurately, then divide and use native toFixed():
        output = (roundingFunction(value * power) / power).toFixed(precision);

        if (optionals) {
            optionalsRegExp = new RegExp('0{1,' + optionals + '}$');
            output = output.replace(optionalsRegExp, '');
        }

        return output;
    }

    /************************************
        Formatting
    ************************************/

    // determine what type of formatting we need to do
    function formatNumeral (n, format, roundingFunction) {
        var output;

        // figure out what kind of format we are dealing with
        if (format.indexOf('$') > -1) { // currency!!!!!
            output = formatCurrency(n, format, roundingFunction);
        } else if (format.indexOf('%') > -1) { // percentage
            output = formatPercentage(n, format, roundingFunction);
        } else if (format.indexOf(':') > -1) { // time
            output = formatTime(n, format);
        } else { // plain ol' numbers or bytes
            output = formatNumber(n._value, format, roundingFunction);
        }

        // return string
        return output;
    }

    function formatCurrency (n, format, roundingFunction) {
        var symbolIndex = format.indexOf('$'),
            openParenIndex = format.indexOf('('),
            minusSignIndex = format.indexOf('-'),
            space = '',
            spliceIndex,
            output;

        // check for space before or after currency
        if (format.indexOf(' $') > -1) {
            space = ' ';
            format = format.replace(' $', '');
        } else if (format.indexOf('$ ') > -1) {
            space = ' ';
            format = format.replace('$ ', '');
        } else {
            format = format.replace('$', '');
        }

        // format the number
        output = formatNumber(n._value, format, roundingFunction);

        // position the symbol
        if (symbolIndex <= 1) {
            if (output.indexOf('(') > -1 || output.indexOf('-') > -1) {
                output = output.split('');
                spliceIndex = 1;
                if (symbolIndex < openParenIndex || symbolIndex < minusSignIndex){
                    // the symbol appears before the "(" or "-"
                    spliceIndex = 0;
                }
                output.splice(spliceIndex, 0, languages[currentLanguage].currency.symbol + space);
                output = output.join('');
            } else {
                output = languages[currentLanguage].currency.symbol + space + output;
            }
        } else {
            if (output.indexOf(')') > -1) {
                output = output.split('');
                output.splice(-1, 0, space + languages[currentLanguage].currency.symbol);
                output = output.join('');
            } else {
                output = output + space + languages[currentLanguage].currency.symbol;
            }
        }

        return output;
    }

    function formatPercentage (n, format, roundingFunction) {
        var space = '',
            output,
            value = n._value * 100;

        // check for space before %
        if (format.indexOf(' %') > -1) {
            space = ' ';
            format = format.replace(' %', '');
        } else {
            format = format.replace('%', '');
        }

        output = formatNumber(value, format, roundingFunction);

        if (output.indexOf(')') > -1 ) {
            output = output.split('');
            output.splice(-1, 0, space + '%');
            output = output.join('');
        } else {
            output = output + space + '%';
        }

        return output;
    }

    function formatTime (n) {
        var hours = Math.floor(n._value/60/60),
            minutes = Math.floor((n._value - (hours * 60 * 60))/60),
            seconds = Math.round(n._value - (hours * 60 * 60) - (minutes * 60));
        return hours + ':' + ((minutes < 10) ? '0' + minutes : minutes) + ':' + ((seconds < 10) ? '0' + seconds : seconds);
    }

    function formatNumber (value, format, roundingFunction) {
        var negP = false,
            signed = false,
            optDec = false,
            abbr = '',
            abbrK = false, // force abbreviation to thousands
            abbrM = false, // force abbreviation to millions
            abbrB = false, // force abbreviation to billions
            abbrT = false, // force abbreviation to trillions
            abbrForce = false, // force abbreviation
            bytes = '',
            ord = '',
            abs = Math.abs(value),
            suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            min,
            max,
            power,
            w,
            precision,
            thousands,
            d = '',
            neg = false;

        // check if number is zero and a custom zero format has been set
        if (value === 0 && zeroFormat !== null) {
            return zeroFormat;
        } else {
            // see if we should use parentheses for negative number or if we should prefix with a sign
            // if both are present we default to parentheses
            if (format.indexOf('(') > -1) {
                negP = true;
                format = format.slice(1, -1);
            } else if (format.indexOf('+') > -1) {
                signed = true;
                format = format.replace(/\+/g, '');
            }

            // see if abbreviation is wanted
            if (format.indexOf('a') > -1) {
                // check if abbreviation is specified
                abbrK = format.indexOf('aK') >= 0;
                abbrM = format.indexOf('aM') >= 0;
                abbrB = format.indexOf('aB') >= 0;
                abbrT = format.indexOf('aT') >= 0;
                abbrForce = abbrK || abbrM || abbrB || abbrT;

                // check for space before abbreviation
                if (format.indexOf(' a') > -1) {
                    abbr = ' ';
                    format = format.replace(' a', '');
                } else {
                    format = format.replace('a', '');
                }

                if (abs >= Math.pow(10, 12) && !abbrForce || abbrT) {
                    // trillion
                    abbr = abbr + languages[currentLanguage].abbreviations.trillion;
                    value = value / Math.pow(10, 12);
                } else if (abs < Math.pow(10, 12) && abs >= Math.pow(10, 9) && !abbrForce || abbrB) {
                    // billion
                    abbr = abbr + languages[currentLanguage].abbreviations.billion;
                    value = value / Math.pow(10, 9);
                } else if (abs < Math.pow(10, 9) && abs >= Math.pow(10, 6) && !abbrForce || abbrM) {
                    // million
                    abbr = abbr + languages[currentLanguage].abbreviations.million;
                    value = value / Math.pow(10, 6);
                } else if (abs < Math.pow(10, 6) && abs >= Math.pow(10, 3) && !abbrForce || abbrK) {
                    // thousand
                    abbr = abbr + languages[currentLanguage].abbreviations.thousand;
                    value = value / Math.pow(10, 3);
                }
            }

            // see if we are formatting bytes
            if (format.indexOf('b') > -1) {
                // check for space before
                if (format.indexOf(' b') > -1) {
                    bytes = ' ';
                    format = format.replace(' b', '');
                } else {
                    format = format.replace('b', '');
                }

                for (power = 0; power <= suffixes.length; power++) {
                    min = Math.pow(1024, power);
                    max = Math.pow(1024, power+1);

                    if (value >= min && value < max) {
                        bytes = bytes + suffixes[power];
                        if (min > 0) {
                            value = value / min;
                        }
                        break;
                    }
                }
            }

            // see if ordinal is wanted
            if (format.indexOf('o') > -1) {
                // check for space before
                if (format.indexOf(' o') > -1) {
                    ord = ' ';
                    format = format.replace(' o', '');
                } else {
                    format = format.replace('o', '');
                }

                ord = ord + languages[currentLanguage].ordinal(value);
            }

            if (format.indexOf('[.]') > -1) {
                optDec = true;
                format = format.replace('[.]', '.');
            }

            w = value.toString().split('.')[0];
            precision = format.split('.')[1];
            thousands = format.indexOf(',');

            if (precision) {
                if (precision.indexOf('[') > -1) {
                    precision = precision.replace(']', '');
                    precision = precision.split('[');
                    d = toFixed(value, (precision[0].length + precision[1].length), roundingFunction, precision[1].length);
                } else {
                    d = toFixed(value, precision.length, roundingFunction);
                }

                w = d.split('.')[0];

                if (d.split('.')[1].length) {
                    d = languages[currentLanguage].delimiters.decimal + d.split('.')[1];
                } else {
                    d = '';
                }

                if (optDec && Number(d.slice(1)) === 0) {
                    d = '';
                }
            } else {
                w = toFixed(value, null, roundingFunction);
            }

            // format number
            if (w.indexOf('-') > -1) {
                w = w.slice(1);
                neg = true;
            }

            if (thousands > -1) {
                w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + languages[currentLanguage].delimiters.thousands);
            }

            if (format.indexOf('.') === 0) {
                w = '';
            }

            return ((negP && neg) ? '(' : '') + ((!negP && neg) ? '-' : '') + ((!neg && signed) ? '+' : '') + w + d + ((ord) ? ord : '') + ((abbr) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : '');
        }
    }

    /************************************
        Top Level Functions
    ************************************/

    numeral = function (input) {
        if (numeral.isNumeral(input)) {
            input = input.value();
        } else if (input === 0 || typeof input === 'undefined') {
            input = 0;
        }

        return new Numeral(Number(input));
    };

    // version number
    numeral.version = VERSION;

    // compare numeral object
    numeral.isNumeral = function (obj) {
        return obj instanceof Numeral;
    };

    // This function will load languages and then set the global language.  If
    // no arguments are passed in, it will simply return the current global
    // language key.
    numeral.language = function (key, values) {
        if (!key) {
            return currentLanguage;
        }

        if (key && !values) {
            if(!languages[key]) {
                throw new Error('Unknown language : ' + key);
            }
            currentLanguage = key;
        }

        if (values || !languages[key]) {
            loadLanguage(key, values);
        }

        return numeral;
    };

    // This function provides access to the loaded language data.  If
    // no arguments are passed in, it will simply return the current
    // global language object.
    numeral.languageData = function (key) {
        if (!key) {
            return languages[currentLanguage];
        }

        if (!languages[key]) {
            throw new Error('Unknown language : ' + key);
        }

        return languages[key];
    };

    numeral.language('en', {
        delimiters: {
            thousands: ',',
            decimal: '.'
        },
        abbreviations: {
            thousand: 'k',
            million: 'm',
            billion: 'b',
            trillion: 't'
        },
        ordinal: function (number) {
            var b = number % 10;
            return (~~ (number % 100 / 10) === 1) ? 'th' :
                (b === 1) ? 'st' :
                (b === 2) ? 'nd' :
                (b === 3) ? 'rd' : 'th';
        },
        currency: {
            symbol: '$'
        }
    });

    numeral.zeroFormat = function (format) {
        zeroFormat = typeof(format) === 'string' ? format : null;
    };

    numeral.defaultFormat = function (format) {
        defaultFormat = typeof(format) === 'string' ? format : '0.0';
    };

    /************************************
        Helpers
    ************************************/

    function loadLanguage(key, values) {
        languages[key] = values;
    }

    /************************************
        Floating-point helpers
    ************************************/

    // The floating-point helper functions and implementation
    // borrows heavily from sinful.js: http://guipn.github.io/sinful.js/

    /**
     * Array.prototype.reduce for browsers that don't support it
     * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Compatibility
     */
    if ('function' !== typeof Array.prototype.reduce) {
        Array.prototype.reduce = function (callback, opt_initialValue) {
            'use strict';

            if (null === this || 'undefined' === typeof this) {
                // At the moment all modern browsers, that support strict mode, have
                // native implementation of Array.prototype.reduce. For instance, IE8
                // does not support strict mode, so this check is actually useless.
                throw new TypeError('Array.prototype.reduce called on null or undefined');
            }

            if ('function' !== typeof callback) {
                throw new TypeError(callback + ' is not a function');
            }

            var index,
                value,
                length = this.length >>> 0,
                isValueSet = false;

            if (1 < arguments.length) {
                value = opt_initialValue;
                isValueSet = true;
            }

            for (index = 0; length > index; ++index) {
                if (this.hasOwnProperty(index)) {
                    if (isValueSet) {
                        value = callback(value, this[index], index, this);
                    } else {
                        value = this[index];
                        isValueSet = true;
                    }
                }
            }

            if (!isValueSet) {
                throw new TypeError('Reduce of empty array with no initial value');
            }

            return value;
        };
    }

    /**
     * Computes the multiplier necessary to make x >= 1,
     * effectively eliminating miscalculations caused by
     * finite precision.
     */
    function multiplier(x) {
        var parts = x.toString().split('.');
        if (parts.length < 2) {
            return 1;
        }
        return Math.pow(10, parts[1].length);
    }

    /**
     * Given a variable number of arguments, returns the maximum
     * multiplier that must be used to normalize an operation involving
     * all of them.
     */
    function correctionFactor() {
        var args = Array.prototype.slice.call(arguments);
        return args.reduce(function (prev, next) {
            var mp = multiplier(prev),
                mn = multiplier(next);
        return mp > mn ? mp : mn;
        }, -Infinity);
    }

    /************************************
        Numeral Prototype
    ************************************/

    numeral.fn = Numeral.prototype = {

        clone : function () {
            return numeral(this);
        },

        format : function (inputString, roundingFunction) {
            return formatNumeral(this,
                  inputString ? inputString : defaultFormat,
                  (roundingFunction !== undefined) ? roundingFunction : Math.round
              );
        },

        value : function () {
            return this._value;
        },

        valueOf : function () {
            return this._value;
        },

        set : function (value) {
            this._value = Number(value);
            return this;
        },

        add : function (value) {
            var corrFactor = correctionFactor.call(null, this._value, value);
            function cback(accum, curr, currI, O) {
                return accum + corrFactor * curr;
            }
            this._value = [this._value, value].reduce(cback, 0) / corrFactor;
            return this;
        },

        subtract : function (value) {
            var corrFactor = correctionFactor.call(null, this._value, value);
            function cback(accum, curr, currI, O) {
                return accum - corrFactor * curr;
            }
            this._value = [value].reduce(cback, this._value * corrFactor) / corrFactor;
            return this;
        },

        multiply : function (value) {
            function cback(accum, curr, currI, O) {
                var corrFactor = correctionFactor(accum, curr);
                return (accum * corrFactor) * (curr * corrFactor) /
                    (corrFactor * corrFactor);
            }
            this._value = [this._value, value].reduce(cback, 1);
            return this;
        },

        divide : function (value) {
            function cback(accum, curr, currI, O) {
                var corrFactor = correctionFactor(accum, curr);
                return (accum * corrFactor) / (curr * corrFactor);
            }
            this._value = [this._value, value].reduce(cback);
            return this;
        },

        difference : function (value) {
            return Math.abs(numeral(this._value).subtract(value).value());
        }

    };

    /************************************
        Exposing Numeral
    ************************************/

    // CommonJS module is defined
    if (hasModule) {
        module.exports = numeral;
    }

    /*global ender:false */
    if (typeof ender === 'undefined') {
        // here, `this` means `window` in the browser, or `global` on the server
        // add `numeral` as a global object via a string identifier,
        // for Closure Compiler 'advanced' mode
        this['numeral'] = numeral;
    }

    /*global define:false */
    if (typeof define === 'function' && define.amd) {
        define([], function () {
            return numeral;
        });
    }
}).call(this);
<commit_msg>Remove all garbage from numeral<commit_after>/*!
 * numeral.js
 * version : 1.5.3
 * author : Adam Draper
 * license : MIT
 * http://adamwdraper.github.com/Numeral-js/
 */

define(["underscore"], function(_) {

    var languages = {};
    var zeroFormat = null;
    var defaultFormat = '0,0';
    var defaultLanguage = 'en';

    /**
     * Implementation of toFixed() that treats floats more like decimals
     *
     * Fixes binary rounding issues (eg. (0.615).toFixed(2) === '0.61') that present
     * problems for accounting- and finance-related software.
     */
    function toFixed(value, precision, roundingFunction, optionals) {
        var power = Math.pow(10, precision),
            optionalsRegExp,
            output;

        // roundingFunction = (roundingFunction !== undefined ? roundingFunction : Math.round);
        // Multiply up by precision, round accurately, then divide and use native toFixed():
        output = (roundingFunction(value * power) / power).toFixed(precision);

        if (optionals) {
            optionalsRegExp = new RegExp('0{1,' + optionals + '}$');
            output = output.replace(optionalsRegExp, '');
        }

        return output;
    }

    // determine what type of formatting we need to do
    function formatNumeral(value, format, language, roundingFunction) {
        var output;

        // figure out what kind of format we are dealing with
        if (format.indexOf('$') > -1) { // currency!!!!!
            output = formatCurrency(value, format, language, roundingFunction);
        } else if (format.indexOf('%') > -1) { // percentage
            output = formatPercentage(value, format, language, roundingFunction);
        } else if (format.indexOf(':') > -1) { // time
            output = formatTime(value, format, language);
        } else { // plain ol' numbers or bytes
            output = formatNumber(value, format, language, roundingFunction);
        }

        return output;
    }

    function formatCurrency(value, format, language, roundingFunction) {
        var symbolIndex = format.indexOf('$'),
            openParenIndex = format.indexOf('('),
            minusSignIndex = format.indexOf('-'),
            space = '',
            spliceIndex,
            output;

        // check for space before or after currency
        if (format.indexOf(' $') > -1) {
            space = ' ';
            format = format.replace(' $', '');
        } else if (format.indexOf('$ ') > -1) {
            space = ' ';
            format = format.replace('$ ', '');
        } else {
            format = format.replace('$', '');
        }

        // format the number
        output = formatNumber(value, format, language, roundingFunction);

        // position the symbol
        if (symbolIndex <= 1) {
            if (output.indexOf('(') > -1 || output.indexOf('-') > -1) {
                output = output.split('');
                spliceIndex = 1;
                if (symbolIndex < openParenIndex || symbolIndex < minusSignIndex){
                    // the symbol appears before the "(" or "-"
                    spliceIndex = 0;
                }
                output.splice(spliceIndex, 0, language.currency.symbol + space);
                output = output.join('');
            } else {
                output = language.currency.symbol + space + output;
            }
        } else {
            if (output.indexOf(')') > -1) {
                output = output.split('');
                output.splice(-1, 0, space + language.currency.symbol);
                output = output.join('');
            } else {
                output = output + space + language.currency.symbol;
            }
        }

        return output;
    }

    function formatPercentage(value, format, language, roundingFunction) {
        var space = '',
            output,
            value = value * 100;

        // check for space before %
        if (format.indexOf(' %') > -1) {
            space = ' ';
            format = format.replace(' %', '');
        } else {
            format = format.replace('%', '');
        }

        output = formatNumber(value, format, language, roundingFunction);

        if (output.indexOf(')') > -1 ) {
            output = output.split('');
            output.splice(-1, 0, space + '%');
            output = output.join('');
        } else {
            output = output + space + '%';
        }

        return output;
    }

    function formatTime(value, language) {
        var hours = Math.floor(value/60/60),
            minutes = Math.floor((value - (hours * 60 * 60))/60),
            seconds = Math.round(value - (hours * 60 * 60) - (minutes * 60));
        return hours + ':' + ((minutes < 10) ? '0' + minutes : minutes) + ':' + ((seconds < 10) ? '0' + seconds : seconds);
    }

    function formatNumber(value, format, language, roundingFunction) {
        var negP = false,
            signed = false,
            optDec = false,
            abbr = '',
            abbrK = false, // force abbreviation to thousands
            abbrM = false, // force abbreviation to millions
            abbrB = false, // force abbreviation to billions
            abbrT = false, // force abbreviation to trillions
            abbrForce = false, // force abbreviation
            bytes = '',
            ord = '',
            abs = Math.abs(value),
            suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            min,
            max,
            power,
            w,
            precision,
            thousands,
            d = '',
            neg = false;

        // check if number is zero and a custom zero format has been set
        if (value === 0 && zeroFormat !== null) {
            return zeroFormat;
        } else {
            // see if we should use parentheses for negative number or if we should prefix with a sign
            // if both are present we default to parentheses
            if (format.indexOf('(') > -1) {
                negP = true;
                format = format.slice(1, -1);
            } else if (format.indexOf('+') > -1) {
                signed = true;
                format = format.replace(/\+/g, '');
            }

            // see if abbreviation is wanted
            if (format.indexOf('a') > -1) {
                // check if abbreviation is specified
                abbrK = format.indexOf('aK') >= 0;
                abbrM = format.indexOf('aM') >= 0;
                abbrB = format.indexOf('aB') >= 0;
                abbrT = format.indexOf('aT') >= 0;
                abbrForce = abbrK || abbrM || abbrB || abbrT;

                // check for space before abbreviation
                if (format.indexOf(' a') > -1) {
                    abbr = ' ';
                    format = format.replace(' a', '');
                } else {
                    format = format.replace('a', '');
                }

                if (abs >= Math.pow(10, 12) && !abbrForce || abbrT) {
                    // trillion
                    abbr = abbr + language.abbreviations.trillion;
                    value = value / Math.pow(10, 12);
                } else if (abs < Math.pow(10, 12) && abs >= Math.pow(10, 9) && !abbrForce || abbrB) {
                    // billion
                    abbr = abbr + language.abbreviations.billion;
                    value = value / Math.pow(10, 9);
                } else if (abs < Math.pow(10, 9) && abs >= Math.pow(10, 6) && !abbrForce || abbrM) {
                    // million
                    abbr = abbr + language.abbreviations.million;
                    value = value / Math.pow(10, 6);
                } else if (abs < Math.pow(10, 6) && abs >= Math.pow(10, 3) && !abbrForce || abbrK) {
                    // thousand
                    abbr = abbr + language.abbreviations.thousand;
                    value = value / Math.pow(10, 3);
                }
            }

            // see if we are formatting bytes
            if (format.indexOf('b') > -1) {
                // check for space before
                if (format.indexOf(' b') > -1) {
                    bytes = ' ';
                    format = format.replace(' b', '');
                } else {
                    format = format.replace('b', '');
                }

                for (power = 0; power <= suffixes.length; power++) {
                    min = Math.pow(1024, power);
                    max = Math.pow(1024, power+1);

                    if (value >= min && value < max) {
                        bytes = bytes + suffixes[power];
                        if (min > 0) {
                            value = value / min;
                        }
                        break;
                    }
                }
            }

            // see if ordinal is wanted
            if (format.indexOf('o') > -1) {
                // check for space before
                if (format.indexOf(' o') > -1) {
                    ord = ' ';
                    format = format.replace(' o', '');
                } else {
                    format = format.replace('o', '');
                }

                ord = ord + language.ordinal(value);
            }

            if (format.indexOf('[.]') > -1) {
                optDec = true;
                format = format.replace('[.]', '.');
            }

            w = value.toString().split('.')[0];
            precision = format.split('.')[1];
            thousands = format.indexOf(',');

            if (precision) {
                if (precision.indexOf('[') > -1) {
                    precision = precision.replace(']', '');
                    precision = precision.split('[');
                    d = toFixed(value, (precision[0].length + precision[1].length), roundingFunction, precision[1].length);
                } else {
                    d = toFixed(value, precision.length, roundingFunction);
                }

                w = d.split('.')[0];

                if (d.split('.')[1].length) {
                    d = language.delimiters.decimal + d.split('.')[1];
                } else {
                    d = '';
                }

                if (optDec && Number(d.slice(1)) === 0) {
                    d = '';
                }
            } else {
                w = toFixed(value, null, roundingFunction);
            }

            // format number
            if (w.indexOf('-') > -1) {
                w = w.slice(1);
                neg = true;
            }

            if (thousands > -1) {
                w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + language.delimiters.thousands);
            }

            if (format.indexOf('.') === 0) {
                w = '';
            }

            return ((negP && neg) ? '(' : '') + ((!negP && neg) ? '-' : '') + ((!neg && signed) ? '+' : '') + w + d + ((ord) ? ord : '') + ((abbr) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : '');
        }
    }

    function addLanguage(key, spec) {
        languages[key] = spec;
    }

    addLanguage('en', {
        delimiters: {
            thousands: ',',
            decimal:   '.'
        },
        abbreviations: {
            thousand: 'k',
            million:  'm',
            billion:  'b',
            trillion: 't'
        },
        ordinal: function (number) {
            var b = number % 10;
            return (~~ (number % 100 / 10) === 1) ? 'th' :
                (b === 1) ? 'st' :
                (b === 2) ? 'nd' :
                (b === 3) ? 'rd' : 'th';
        },
        currency: {
            symbol: '$'
        }
    });

    function format(input, formatString, language, roundingFunction) {
        return formatNumeral(
            Number(input),
            _.isString(formatString)        ? formatString        : defaultFormat,
            _.isString(language)            ? languages[language] : languages[defaultLanguage],
            _.isUndefined(roundingFunction) ? Math.round          : roundingFunction);
    }

    return {"format": format};
});
<|endoftext|>


<commit_before>function displaySeriesNumber() {
  var s = document.getElementById("id_series");
  var name = s.options[s.selectedIndex].text;

  var num = document.getElementById("id_number");
  if (num.value = '') {
    document.getElementById("seriesNumber").style.visibility = "hidden"
  }

  if (name != '---------') {
    document.getElementById("seriesNumber").style.visibility = "visible";
  }
  else {
      var num = document.getElementById("id_number");
      num.value = '';
      document.getElementById("seriesNumber").style.visibility = "hidden";
  }
}

document.onkeyup=function(e){
  var e = e || window.event; // for IE to cover IEs window event-object
  // shift + to add a book
  if(e.shiftKey && e.which == 107) {
    document.getElementById('add-book').click();
  }
}
<commit_msg>Press 'a' to add a book but not on the book form page (obviously)<commit_after>function displaySeriesNumber() {
  var s = document.getElementById("id_series");
  var name = s.options[s.selectedIndex].text;

  var num = document.getElementById("id_number");
  if (num.value = '') {
    document.getElementById("seriesNumber").style.visibility = "hidden"
  }

  if (name != '---------') {
    document.getElementById("seriesNumber").style.visibility = "visible";
  }
  else {
      var num = document.getElementById("id_number");
      num.value = '';
      document.getElementById("seriesNumber").style.visibility = "hidden";
  }
}

document.onkeyup=function(e){
  var e = e || window.event; // for IE to cover IEs window event-object
  // + to add a book
  if (e.which == '65' && document.getElementsByClassName('book-form').length == 0) {
    document.getElementById('add-book').click();
  }
}
<|endoftext|>


<commit_before>/**
 * Copyright 2013-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactOwner
 */

'use strict';

var invariant = require('invariant');

/**
 * ReactOwners are capable of storing references to owned components.
 *
 * All components are capable of //being// referenced by owner components, but
 * only ReactOwner components are capable of //referencing// owned components.
 * The named reference is known as a "ref".
 *
 * Refs are available when mounted and updated during reconciliation.
 *
 *   var MyComponent = React.createClass({
 *     render: function() {
 *       return (
 *

 *           <CustomComponent ref="custom" />
 *

 *       );
 *     },
 *     handleClick: function() {
 *       this.refs.custom.handleClick();
 *     },
 *     componentDidMount: function() {
 *       this.refs.custom.initialize();
 *     }
 *   });
 *
 * Refs should rarely be used. When refs are used, they should only be done to
 * control data that is not handled by React's data flow.
 *
 * @class ReactOwner
 */
var ReactOwner = {

  /**
   * @param {?object} object
   * @return {boolean} True if `object` is a valid owner.
   * @final
   */
  isValidOwner: function(object) {
    return !!(
      object &&
      typeof object.attachRef === 'function' &&
      typeof object.detachRef === 'function'
    );
  },

  /**
   * Adds a component by ref to an owner component.
   *
   * @param {ReactComponent} component Component to reference.
   * @param {string} ref Name by which to refer to the component.
   * @param {ReactOwner} owner Component on which to record the ref.
   * @final
   * @internal
   */
  addComponentAsRefTo: function(component, ref, owner) {
    invariant(
      ReactOwner.isValidOwner(owner),
      'addComponentAsRefTo(...): Only a ReactOwner can have refs. This ' +
      'usually means that you\'re trying to add a ref to a component that ' +
      'doesn\'t have an owner (that is, was not created inside of another ' +
      'component\'s `render` method). Try rendering this component inside of ' +
      'a new top-level component which will hold the ref.'
    );
    owner.attachRef(ref, component);
  },

  /**
   * Removes a component by ref from an owner component.
   *
   * @param {ReactComponent} component Component to dereference.
   * @param {string} ref Name of the ref to remove.
   * @param {ReactOwner} owner Component on which the ref is recorded.
   * @final
   * @internal
   */
  removeComponentAsRefFrom: function(component, ref, owner) {
    invariant(
      ReactOwner.isValidOwner(owner),
      'removeComponentAsRefFrom(...): Only a ReactOwner can have refs. This ' +
      'usually means that you\'re trying to remove a ref to a component that ' +
      'doesn\'t have an owner (that is, was not created inside of another ' +
      'component\'s `render` method). Try rendering this component inside of ' +
      'a new top-level component which will hold the ref.'
    );
    // Check that `component` is still the current ref because we do not want to
    // detach the ref if another component stole it.
    if (owner.getPublicInstance().refs[ref] === component.getPublicInstance()) {
      owner.detachRef(ref);
    }
  },

};

module.exports = ReactOwner;
<commit_msg>Fix typo in removeComponentAsRefFrom error message.<commit_after>/**
 * Copyright 2013-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactOwner
 */

'use strict';

var invariant = require('invariant');

/**
 * ReactOwners are capable of storing references to owned components.
 *
 * All components are capable of //being// referenced by owner components, but
 * only ReactOwner components are capable of //referencing// owned components.
 * The named reference is known as a "ref".
 *
 * Refs are available when mounted and updated during reconciliation.
 *
 *   var MyComponent = React.createClass({
 *     render: function() {
 *       return (
 *

 *           <CustomComponent ref="custom" />
 *

 *       );
 *     },
 *     handleClick: function() {
 *       this.refs.custom.handleClick();
 *     },
 *     componentDidMount: function() {
 *       this.refs.custom.initialize();
 *     }
 *   });
 *
 * Refs should rarely be used. When refs are used, they should only be done to
 * control data that is not handled by React's data flow.
 *
 * @class ReactOwner
 */
var ReactOwner = {

  /**
   * @param {?object} object
   * @return {boolean} True if `object` is a valid owner.
   * @final
   */
  isValidOwner: function(object) {
    return !!(
      object &&
      typeof object.attachRef === 'function' &&
      typeof object.detachRef === 'function'
    );
  },

  /**
   * Adds a component by ref to an owner component.
   *
   * @param {ReactComponent} component Component to reference.
   * @param {string} ref Name by which to refer to the component.
   * @param {ReactOwner} owner Component on which to record the ref.
   * @final
   * @internal
   */
  addComponentAsRefTo: function(component, ref, owner) {
    invariant(
      ReactOwner.isValidOwner(owner),
      'addComponentAsRefTo(...): Only a ReactOwner can have refs. This ' +
      'usually means that you\'re trying to add a ref to a component that ' +
      'doesn\'t have an owner (that is, was not created inside of another ' +
      'component\'s `render` method). Try rendering this component inside of ' +
      'a new top-level component which will hold the ref.'
    );
    owner.attachRef(ref, component);
  },

  /**
   * Removes a component by ref from an owner component.
   *
   * @param {ReactComponent} component Component to dereference.
   * @param {string} ref Name of the ref to remove.
   * @param {ReactOwner} owner Component on which the ref is recorded.
   * @final
   * @internal
   */
  removeComponentAsRefFrom: function(component, ref, owner) {
    invariant(
      ReactOwner.isValidOwner(owner),
      'removeComponentAsRefFrom(...): Only a ReactOwner can have refs. This ' +
      'usually means that you\'re trying to remove a ref from a component that ' +
      'doesn\'t have an owner (that is, was not created inside of another ' +
      'component\'s `render` method). Try rendering this component inside of ' +
      'a new top-level component which will hold the ref.'
    );
    // Check that `component` is still the current ref because we do not want to
    // detach the ref if another component stole it.
    if (owner.getPublicInstance().refs[ref] === component.getPublicInstance()) {
      owner.detachRef(ref);
    }
  },

};

module.exports = ReactOwner;
<|endoftext|>


<commit_before>;(function (window, factory) {

  factory((window.ImageBrowser = {}))

  ImageBrowser.init()

}(window, (function (exports) {

const ImageBrowser = function () {

  this.images = []

  this.current = 0

  this.show = false

  this._dom

  this._titleDOM

  this._pagnationDOM

  this.init()
}

ImageBrowser.prototype = {

  constructor: ImageBrowser,

  mount (dom) {
    dom.appendChild(this._dom)
    return this
  },

  showOnClick (dom) {
    if (dom == void 0) {
      return
    }

    const imageSwiper = this._dom.getElementsByClassName('image-browser__image-swiper')[0]
    while(imageSwiper.firstChild) imageSwiper.removeChild(imageSwiper.firstChild)

    const images = [...dom.getElementsByTagName('a')].map((link, i) => {
      const url = link.dataset.url || link.getAttribute('href')
      const text = link.innerText
      link.setAttribute('href', 'javascript:void(0);')
      link.dataset.url = url
      link.dataset.index = i
      return { url, text }
    })
    this.insertImages(images)
    this.images = images

    const clickHandler = (e) => {
      const event = e || window.event
      const target = event.target || event.srcElement
      if (target.nodeName.toLocaleLowerCase() !== 'a') {
        return
      }
      this.toggleClose()

      const current = +target.dataset.index
      this.current = current

      const len = this.images.length

      ;[(current - 1 + len) % len, current, (current + 1) % len].forEach(i => {
        const image = this.images[i]
        if (image && image.dom.nodeName.toLocaleLowerCase() === 'img') {
          image.dom.setAttribute('src', image.url)
        }
      })

      imageSwiper.scrollLeft = current * 1000

      this._titleDOM.textContent = this.images[current].text
      this._pagnationDOM.textContent = `${current + 1}/${len}`

      const arrowBtns = this._dom.getElementsByClassName('image-browser__arrow-btn')
      if (current == 0) {
        arrowBtns[0].classList.add('image-browser__btn_disable')
      }

      if (current == len - 1) {
        arrowBtns[1].classList.add('image-browser__btn_disable')
      }
    }

    dom.addEventListener('click', clickHandler, false)
  },

  toggleClose () {
    const dom = this._dom
    if (this.show) {
      this.show = false
      dom.classList.remove('image-browser__show')
      this.current = 0
    }
    else {
      this.show = true
      dom.classList.add('image-browser__show')
    }
  },

  lazyLoadImage (index) {
    const image = this.images[index]
    if (image == void 0) {
      return
    }
    const { url, dom } = image

    if (dom.nodeName.toLocaleLowerCase() !== 'img') {
      return
    }

    if (dom.getAttribute('src') !== null) {
      return
    }
    dom.setAttribute('src', url)
  },

  init () {
    const browserWrapper = create('div', 'image-browser')
    browserWrapper.appendChild(this.createToolbar())
    browserWrapper.appendChild(this.createImageNav())

    const pagnation = create('div', 'image-browser__pagnation')
    pagnation.textContent = '0/0'
    this._pagnationDOM = browserWrapper.appendChild(pagnation)

    this._dom = browserWrapper

    this.mount(document.body)

    const ext = document.getElementsByClassName('ext')[0]
    this.showOnClick(ext)
  },

  createToolbar () {
    const toolbar = create('div', 'image-browser__row image-browser__toolbar')

    const $1 = create('div', 'image-browser__toolbar-item image-browser__toolbar-item_left')
    const rotateLeftBtn = create('button', 'image-browser__btn image-browser__rotate-btn')
    rotateLeftBtn.textContent = '向左旋转'
    rotateLeftBtn.onclick = () => { rotate(this.images[this.current].dom, -90) }
    $1.appendChild(rotateLeftBtn)
    const rotateRightBtn = create('button', 'image-browser__btn image-browser__rotate-btn')
    rotateRightBtn.textContent = '向右旋转'
    rotateRightBtn.onclick = () => { rotate(this.images[this.current].dom, 90) }
    $1.appendChild(rotateRightBtn)

    const $2 = create('div', 'image-browser__toolbar-item image-browser__toolbar-item_center')
    const title = create('div', 'image-browser__title')
    title.textContent = '文件名'
    this._titleDOM = $2.appendChild(title)

    const $3 = create('div', 'image-browser__toolbar-item image-browser__toolbar-item_right')
    const downloadBtn = create('button', 'image-browser__btn image-browser__download-btn')
    downloadBtn.textContent = '下载'
    downloadBtn.onclick = () => {
      const { text, url } = this.images[this.current]
      const xhr = new XMLHttpRequest()
      xhr.open('GET', url)
      xhr.responseType = 'blob'
      xhr.onload = e => {
        const blob = new Blob([xhr.response], { type: xhr.response.type })
        const reader = new FileReader()
        reader.onload = () => {
          const tempLink = create('a')
          tempLink.style.display = 'none'
          this._dom.appendChild(tempLink)
          // const url = URL.createObjectURL(xhr.response)
          tempLink.href = reader.result
          tempLink.download = text
          tempLink.click()
          // URL.revokeObjectURL(url)
          this._dom.removeChild(tempLink)
        }
        reader.readAsDataURL(blob)
      }
      xhr.send()
    }
    $3.appendChild(downloadBtn)
    const closeBtn = create('button', 'image-browser__btn image-browser__close-btn ion-ios-close-circle-outline')
    closeBtn.onclick = this.toggleClose.bind(this)
    $3.appendChild(closeBtn);

    [$1, $2, $3].forEach(e => toolbar.appendChild(e))

    return toolbar
  },

  createImageNav () {
    const imageNav = create('div', 'image-browser__row image-browser__image-nav')

    const imageSwiper = create('div', 'image-browser__image-swiper')
    const createNavigatorBtn = arrow => {
      const btn = create('button', `image-browser__btn image-browser__arrow-btn ion-ios-arrow-${arrow}`)
      return btn
    }

    const backBtn = createNavigatorBtn('back')
    const forwardBtn = createNavigatorBtn('forward')

    ;[backBtn, imageSwiper, forwardBtn].forEach(e => imageNav.appendChild(e))

    const switchImage = (step) => {
      return () => {
        const len = this.images.length
        const current = (this.current + step + len) % len
        imageSwiper.scrollLeft = current * 1000
        this._titleDOM.textContent = this.images[current].text
        this._pagnationDOM.textContent = `${current + 1}/${len}`
        this.lazyLoadImage((current + step + len) % len)
        this.current = current
      }
    }

    const previous = switchImage(-1)
    const next = switchImage(1)

    backBtn.onclick = previous
    forwardBtn.onclick = next

    return imageNav
  },

  createImgDOM ({ src, index }) {
    const image = create('img', 'image-browser__image')
    image.setAttribute('alt', src)
    image.style = '-webkit-transform:rotate(0);transform:rotate(0)'
    image.dataset.index = index

    return image
  },

  createUnknownFileTypeDOM (index) {
    const div = create('div', 'image-browser__image-placeholder')
    div.textContent = '该文件不是图片，点击下载进行查看'
    div.dataset.index = index
    return div
  },

  insertImages (images) {
    const imageSwiper = this._dom.getElementsByClassName('image-browser__image-swiper')[0]
    const extensions = ['png', 'jpg', 'jpeg', 'gif', 'webp']
    images.forEach((e, index) => {
      let dom = extensions.includes(getFileExtension(e.text).toLocaleLowerCase()) ?
        this.createImgDOM({ src: e.url, index }) :
        this.createUnknownFileTypeDOM(index)

      const imageWrapper = create('div', 'image-browser__image-wrapper')
      imageWrapper.style.left = `${index * 1000}px`

      imageWrapper.appendChild(dom)
      imageSwiper.appendChild(imageWrapper)

      e.dom = dom
    })
  }
}

function getFileExtension (filename) {
  return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : void 0
}

function create (tag, className) {
  const dom = document.createElement(tag)
  className && (dom.className = className)
  return dom
}

function rotate (dom, deg) {
  const matrix = window.getComputedStyle(dom, null).getPropertyValue('transform')
  const [a, b] = matrix.split('(')[1].split(')')[0].split(',')
  const _deg = Math.round(Math.atan2(b, a) * (180 / Math.PI))
  dom.style = `-webkit-transform:rotate(${_deg + deg}deg);transform:rotate(${_deg + deg}deg)`
}

function scroll ({ dom, offset }) {
  dom.scrollLeft += offset
}

const init = function () {
  let sharedInstance = null
  return function () {
    return sharedInstance || (sharedInstance = new ImageBrowser())
  }
}()

function showOnClick (dom) {
  init().showOnClick(dom)
}

exports.init = init
exports.showOnClick = showOnClick
})));<commit_msg>fix 非图片附件查看<commit_after>;(function (window, factory) {

  factory((window.ImageBrowser = {}))

  ImageBrowser.init()

}(window, (function (exports) {

const ImageBrowser = function () {

  this.images = []

  this.current = 0

  this.show = false

  this._dom

  this._titleDOM

  this._pagnationDOM

  this.init()
}

ImageBrowser.prototype = {

  constructor: ImageBrowser,

  mount (dom) {
    dom.appendChild(this._dom)
    return this
  },

  showOnClick (dom) {
    if (dom == void 0) {
      return
    }

    const imageSwiper = this._dom.getElementsByClassName('image-browser__image-swiper')[0]
    while(imageSwiper.firstChild) imageSwiper.removeChild(imageSwiper.firstChild)

    const images = [...dom.getElementsByTagName('a')].map((link, i) => {
      const url = link.dataset.url || link.getAttribute('href')
      const text = link.innerText
      link.setAttribute('href', 'javascript:void(0);')
      link.dataset.url = url
      link.dataset.index = i
      return { url, text }
    })
    this.insertImages(images)
    this.images = images

    const clickHandler = (e) => {
      const event = e || window.event
      const target = event.target || event.srcElement
      if (target.nodeName.toLocaleLowerCase() !== 'a') {
        return
      }
      this.toggleClose()

      const current = +target.dataset.index
      this.current = current

      const len = this.images.length

      ;[(current - 1 + len) % len, current, (current + 1) % len].forEach(i => {
        const image = this.images[i]
        if (image && image.dom.nodeName.toLocaleLowerCase() === 'img') {
          image.dom.setAttribute('src', image.url)
        }
      })

      imageSwiper.scrollLeft = current * 1000

      this._titleDOM.textContent = this.images[current].text
      this._pagnationDOM.textContent = `${current + 1}/${len}`

      const arrowBtns = this._dom.getElementsByClassName('image-browser__arrow-btn')
      if (current == 0) {
        arrowBtns[0].classList.add('image-browser__btn_disable')
      }

      if (current == len - 1) {
        arrowBtns[1].classList.add('image-browser__btn_disable')
      }
    }

    dom.addEventListener('click', clickHandler, false)
  },

  toggleClose () {
    const dom = this._dom
    if (this.show) {
      this.show = false
      dom.classList.remove('image-browser__show')
      this.current = 0
    }
    else {
      this.show = true
      dom.classList.add('image-browser__show')
    }
  },

  lazyLoadImage (index) {
    const image = this.images[index]
    if (image == void 0) {
      return
    }
    const { url, dom } = image

    if (dom.nodeName.toLocaleLowerCase() !== 'img') {
      return
    }

    if (dom.getAttribute('src') !== null) {
      return
    }
    dom.setAttribute('src', url)
  },

  init () {
    const browserWrapper = create('div', 'image-browser')
    browserWrapper.appendChild(this.createToolbar())
    browserWrapper.appendChild(this.createImageNav())

    const pagnation = create('div', 'image-browser__pagnation')
    pagnation.textContent = '0/0'
    this._pagnationDOM = browserWrapper.appendChild(pagnation)

    this._dom = browserWrapper

    this.mount(document.body)

    const ext = document.getElementsByClassName('ext')[0]
    this.showOnClick(ext)
  },

  createToolbar () {
    const toolbar = create('div', 'image-browser__row image-browser__toolbar')

    const $1 = create('div', 'image-browser__toolbar-item image-browser__toolbar-item_left')
    const rotateLeftBtn = create('button', 'image-browser__btn image-browser__rotate-btn')
    rotateLeftBtn.textContent = '向左旋转'
    rotateLeftBtn.onclick = () => { rotate(this.images[this.current].dom, -90) }
    $1.appendChild(rotateLeftBtn)
    const rotateRightBtn = create('button', 'image-browser__btn image-browser__rotate-btn')
    rotateRightBtn.textContent = '向右旋转'
    rotateRightBtn.onclick = () => { rotate(this.images[this.current].dom, 90) }
    $1.appendChild(rotateRightBtn)

    const $2 = create('div', 'image-browser__toolbar-item image-browser__toolbar-item_center')
    const title = create('div', 'image-browser__title')
    title.textContent = '文件名'
    this._titleDOM = $2.appendChild(title)

    const $3 = create('div', 'image-browser__toolbar-item image-browser__toolbar-item_right')
    const downloadBtn = create('button', 'image-browser__btn image-browser__download-btn')
    downloadBtn.textContent = '下载'
    downloadBtn.onclick = () => {
      const { text, url } = this.images[this.current]
      const xhr = new XMLHttpRequest()
      xhr.open('GET', url)
      xhr.responseType = 'blob'
      xhr.onload = e => {
        const blob = new Blob([xhr.response], { type: xhr.response.type })
        const reader = new FileReader()
        reader.onload = () => {
          const tempLink = create('a')
          tempLink.style.display = 'none'
          this._dom.appendChild(tempLink)
          // const url = URL.createObjectURL(xhr.response)
          tempLink.href = reader.result
          tempLink.download = text
          tempLink.click()
          // URL.revokeObjectURL(url)
          this._dom.removeChild(tempLink)
        }
        reader.readAsDataURL(blob)
      }
      xhr.send()
    }
    $3.appendChild(downloadBtn)
    const closeBtn = create('button', 'image-browser__btn image-browser__close-btn ion-ios-close-circle-outline')
    closeBtn.onclick = this.toggleClose.bind(this)
    $3.appendChild(closeBtn);

    [$1, $2, $3].forEach(e => toolbar.appendChild(e))

    return toolbar
  },

  createImageNav () {
    const imageNav = create('div', 'image-browser__row image-browser__image-nav')

    const imageSwiper = create('div', 'image-browser__image-swiper')
    const createNavigatorBtn = arrow => {
      const btn = create('button', `image-browser__btn image-browser__arrow-btn ion-ios-arrow-${arrow}`)
      return btn
    }

    const backBtn = createNavigatorBtn('back')
    const forwardBtn = createNavigatorBtn('forward')

    ;[backBtn, imageSwiper, forwardBtn].forEach(e => imageNav.appendChild(e))

    const switchImage = (step) => {
      return () => {
        const len = this.images.length
        const current = (this.current + step + len) % len
        imageSwiper.scrollLeft = current * 1000
        this._titleDOM.textContent = this.images[current].text
        this._pagnationDOM.textContent = `${current + 1}/${len}`
        this.lazyLoadImage((current + step + len) % len)
        this.current = current
      }
    }

    const previous = switchImage(-1)
    const next = switchImage(1)

    backBtn.onclick = previous
    forwardBtn.onclick = next

    return imageNav
  },

  createImgDOM ({ src, index }) {
    const image = create('img', 'image-browser__image')
    image.setAttribute('alt', src)
    image.style = '-webkit-transform:rotate(0);transform:rotate(0)'
    image.dataset.index = index

    return image
  },

  createUnknownFileTypeDOM (url,index) {
    const div = create('div', 'image-browser__image-placeholder')
    div.innerHTML = '预览或下载'
    div.dataset.index = index
    return div
  },

  insertImages (images) {
    const imageSwiper = this._dom.getElementsByClassName('image-browser__image-swiper')[0]
    const extensions = ['png', 'jpg', 'jpeg', 'gif', 'webp']
    images.forEach((e, index) => {
      let dom = extensions.includes(getFileExtension(e.text).toLocaleLowerCase()) ?
        this.createImgDOM({ src: e.url, index }) :
        this.createUnknownFileTypeDOM(e.url,index)

      const imageWrapper = create('div', 'image-browser__image-wrapper')
      imageWrapper.style.left = `${index * 1000}px`

      imageWrapper.appendChild(dom)
      imageSwiper.appendChild(imageWrapper)

      e.dom = dom
    })
  }
}

function getFileExtension (filename) {
  return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : void 0
}

function create (tag, className) {
  const dom = document.createElement(tag)
  className && (dom.className = className)
  return dom
}

function rotate (dom, deg) {
  const matrix = window.getComputedStyle(dom, null).getPropertyValue('transform')
  const [a, b] = matrix.split('(')[1].split(')')[0].split(',')
  const _deg = Math.round(Math.atan2(b, a) * (180 / Math.PI))
  dom.style = `-webkit-transform:rotate(${_deg + deg}deg);transform:rotate(${_deg + deg}deg)`
}

function scroll ({ dom, offset }) {
  dom.scrollLeft += offset
}

const init = function () {
  let sharedInstance = null
  return function () {
    return sharedInstance || (sharedInstance = new ImageBrowser())
  }
}()

function showOnClick (dom) {
  init().showOnClick(dom)
}

exports.init = init
exports.showOnClick = showOnClick
})));<|endoftext|>


<commit_before>/**
 * @private
 * @noflow
 */

import EventEmitter from 'events';
import net from 'net';
import createLog from 'silk-log/device';
import * as util from 'silk-sysutils';

const log = createLog('sensors');

const SENSORS_SOCKET_NAME = '/dev/socket/sensors';

/**
 * Type of sensors to interact with. For a full list of
 * sensor types please refer to the source code.
 *
 * @property {(ACCELEROMETER|LIGHT|PROXIMITY)} SENSOR_TYPE
 * @memberof silk-sensors
 */
let SENSOR_TYPE = {
  ACCELEROMETER: 1,
  MAGNETIC_FIELD: 2,
  ORIENTATION: 3,
  GYROSCOPE: 4,
  LIGHT: 5,
  PRESSURE: 6,
  TEMPERATURE: 7,
  PROXIMITY: 8,
  GRAVITY: 9,
  LINEAR_ACCELERATION: 10,
  ROTATION_VECTOR: 11,
  RELATIVE_HUMIDITY: 12,
  AMBIENT_TEMPERATURE: 13,
  MAGNETIC_FIELD_UNCALIBRATED: 14,
  GAME_ROTATION_VECTOR: 15,
  GYROSCOPE_UNCALIBRATED: 16,
  SIGNIFICANT_MOTION: 17,
  STEP_DETECTOR: 18,
  STEP_COUNTER: 19,
  GEOMAGNETIC_ROTATION_VECTOR: 20,
  HEART_RATE: 21,
  TILT_DETECTOR: 22,
  WAKE_GESTURE: 23,
  GLANCE_GESTURE: 24,
  PICK_UP_GESTURE: 25,
};

/**
 * This module exposes functionality to activate/deactivate and read sensors
 * data from various sensors available on the device.
 * @module silk-sensors
 * @example
 * const sensors = require('silk-sensors');
 *
 * sensors.init();
 * sensors.on('initialize', () => {
 *  sensors.activate(sensors.SENSOR_TYPE.LIGHT, 250);
 * });
 * sensors.on('data', (sensorEvent) => {
 *  log.info('Sensor type: ' + sensorEvent.sensorType);
 *  log.info('Sensor values: + sensorEvent.values');
 * });
 */
class Sensors extends EventEmitter {
  constructor() {
    super();
    this._ready = false;
    this._active = false;
  }

  /**
   * Initialize the Sensors module
   * @memberof silk-sensors
   * @instance
   */
  init() {
    log.verbose(`connecting to ${SENSORS_SOCKET_NAME} socket`);
    this._socket = net.createConnection(SENSORS_SOCKET_NAME, () => {
      log.verbose(`connected to ${SENSORS_SOCKET_NAME} socket`);
      this._buffer = '';
      this._ready = true;
      this._command({cmdName: 'ready'});
    });
    this._socket.on('data', data => this._onData(data));
    this._socket.on('error', err => {
      this._restart(`sensors error, reason=${err}`);
    });
    this._socket.on('close', hadError => {
      if (!hadError) {
        this._restart(`sensors close`);
      }
    });
  }

  /**
   * Activate a given sensor
   *
   * @param {number} sensorType SENSOR_TYPE.* constant
   * @param {number} rate Sensor polling rate in milliseconds
   * @memberof silk-sensors
   * @instance
   */
  activate(sensorType, rate) {
    this._active = true;
    this._command({cmdName: 'activate', sensorType, rate});
  }

  /**
   * Dectivate a given sensor
   *
   * @param {number} sensorType SENSOR_TYPE.* constant
   * @memberof silk-sensors
   * @instance
   */
  deactivate(sensorType) {
    this._active = false;
    this._command({cmdName: 'deactivate', sensorType});
  }

  /**
   * @private
   */
  _restart(why) {
    if (this._socket) {
      this._ready = false;
      this._socket = null;

      log.info(`sensors restart: ${why}`);
      util.timeout(1000)
      .then(() => this.init())
      .catch(util.processthrow);
      return;
    }
    log.info(`sensors restart pending (ignored "${why}")`);
  }

  /**
   * @private
   */
  _onData(data) {
    log.debug(`received data: ${data.toString()}`);
    this._buffer += data.toString();

    let nullByte;
    while ((nullByte = this._buffer.indexOf('\0')) !== -1) {
      let line = this._buffer.substring(0, nullByte);
      this._buffer = this._buffer.substring(nullByte + 1);

      let found;
      if ((found = line.match(/^([\d]*) (.*)/))) {
        line = found[2];
      }

      let sensorEvent = JSON.parse(line);
      if (sensorEvent.eventName === 'initialized') {
        /**
         * This event is emitted when sensor class is initialized and is ready
         * to activate/decativate a sensor
         *
         * @event initialize
         * @memberof silk-sensors
         * @instance
         */
        this.emit('initialize');
      } else if (sensorEvent.eventName === 'activated') {
        this.emit('activate');
        this._command({cmdName: 'poll'}); // Start polling
      } else if (sensorEvent.eventName === 'deactivated') {
        this.emit('deactivate');
      } else if (sensorEvent.eventName === 'error') {
        log.warn('Received "error" from sensor service');
      } else if (sensorEvent.eventName === 'data') {
        /**
         * This event is emitted when sensor data is available.
         *
         * @event data
         * @property {number} sensorType One of the SENSOR_TYPE.* constants
         * @property {*[]} values: Sensor data as returned by the hardware sensor. The length and
         *           contents of the values array depends on which sensor type is being
         *           monitored. Please refer to `hardware/sensors.h` for details.
         * @memberof silk-sensors
         * @instance
         */
        this.emit('data', sensorEvent);
      } else {
        log.warn(`Error: Unknown sensors command ${line}`);
      }
    }
  }

  /**
   * @private
   */
  _command(cmd) {
    if (!this._ready) {
      log.warn(`sensors service not ready, ignoring the command`);
      return;
    }

    // sensors socket expects the command data in the following format
    const event = JSON.stringify(cmd) + '\0';
    this._socket.write(event);
    log.verbose(`sensors << ${JSON.stringify(cmd)}`);
  }
}

Sensors.prototype.SENSOR_TYPE = SENSOR_TYPE;

let sensors = new Sensors();
module.exports = sensors;
<commit_msg>Flowify silk-sensors<commit_after>/**
 * @flow
 * @private
 */

import EventEmitter from 'events';
import * as net from 'net';
import createLog from 'silk-log/device';
import * as util from 'silk-sysutils';
import invariant from 'assert';

import type {Socket} from 'net';

const log = createLog('sensors');

const SENSORS_SOCKET_NAME = '/dev/socket/sensors';

/**
 * This module exposes functionality to activate/deactivate and read sensors
 * data from various sensors available on the device.
 * @module silk-sensors
 * @example
 * const sensors = require('silk-sensors');
 *
 * sensors.init();
 * sensors.on('initialize', () => {
 *  sensors.activate(sensors.SENSOR_TYPE.LIGHT, 250);
 * });
 * sensors.on('data', (sensorEvent) => {
 *  log.info('Sensor type: ' + sensorEvent.sensorType);
 *  log.info('Sensor values: + sensorEvent.values');
 * });
 */
export class Sensors extends EventEmitter {
  _active: boolean;
  _buffer: string;
  _ready: boolean;
  _socket: ?Socket;

  /**
   * Type of sensors to interact with. For a full list of
   * sensor types please refer to the source code.
   *
   * @property {(ACCELEROMETER|LIGHT|PROXIMITY)} SENSOR_TYPE
   * @memberof silk-sensors
   */
  SENSOR_TYPE = {
    ACCELEROMETER: 1,
    MAGNETIC_FIELD: 2,
    ORIENTATION: 3,
    GYROSCOPE: 4,
    LIGHT: 5,
    PRESSURE: 6,
    TEMPERATURE: 7,
    PROXIMITY: 8,
    GRAVITY: 9,
    LINEAR_ACCELERATION: 10,
    ROTATION_VECTOR: 11,
    RELATIVE_HUMIDITY: 12,
    AMBIENT_TEMPERATURE: 13,
    MAGNETIC_FIELD_UNCALIBRATED: 14,
    GAME_ROTATION_VECTOR: 15,
    GYROSCOPE_UNCALIBRATED: 16,
    SIGNIFICANT_MOTION: 17,
    STEP_DETECTOR: 18,
    STEP_COUNTER: 19,
    GEOMAGNETIC_ROTATION_VECTOR: 20,
    HEART_RATE: 21,
    TILT_DETECTOR: 22,
    WAKE_GESTURE: 23,
    GLANCE_GESTURE: 24,
    PICK_UP_GESTURE: 25,
  };

  constructor() {
    super();
    this._ready = false;
    this._active = false;
  }

  /**
   * Initialize the Sensors module
   * @memberof silk-sensors
   * @instance
   */
  init() {
    log.verbose(`connecting to ${SENSORS_SOCKET_NAME} socket`);
    const socket = this._socket = net.createConnection(SENSORS_SOCKET_NAME, () => {
      log.verbose(`connected to ${SENSORS_SOCKET_NAME} socket`);
      this._buffer = '';
      this._ready = true;
      this._command({cmdName: 'ready'});
    });
    socket.on('data', data => this._onData(data));
    socket.on('error', err => {
      this._restart(`sensors error, reason=${err}`);
    });
    socket.on('close', hadError => {
      if (!hadError) {
        this._restart(`sensors close`);
      }
    });
  }

  /**
   * Activate a given sensor
   *
   * @param {number} sensorType SENSOR_TYPE.* constant
   * @param {number} rate Sensor polling rate in milliseconds
   * @memberof silk-sensors
   * @instance
   */
  activate(sensorType: number, rate: number) {
    this._active = true;
    this._command({cmdName: 'activate', sensorType, rate});
  }

  /**
   * Dectivate a given sensor
   *
   * @param {number} sensorType SENSOR_TYPE.* constant
   * @memberof silk-sensors
   * @instance
   */
  deactivate(sensorType: number) {
    this._active = false;
    this._command({cmdName: 'deactivate', sensorType});
  }

  /**
   * @private
   */
  _restart(why: string) {
    if (this._socket) {
      this._ready = false;
      this._socket = null;

      log.info(`sensors restart: ${why}`);
      util.timeout(1000)
      .then(() => this.init())
      .catch(util.processthrow);
      return;
    }
    log.info(`sensors restart pending (ignored "${why}")`);
  }

  /**
   * @private
   */
  _onData(data: Buffer) {
    log.debug(`received data: ${data.toString()}`);
    this._buffer += data.toString();

    let nullByte;
    while ((nullByte = this._buffer.indexOf('\0')) !== -1) {
      let line = this._buffer.substring(0, nullByte);
      this._buffer = this._buffer.substring(nullByte + 1);

      let found;
      if ((found = line.match(/^([\d]*) (.*)/))) {
        line = found[2];
      }

      let sensorEvent = JSON.parse(line);
      if (sensorEvent.eventName === 'initialized') {
        /**
         * This event is emitted when sensor class is initialized and is ready
         * to activate/decativate a sensor
         *
         * @event initialize
         * @memberof silk-sensors
         * @instance
         */
        this.emit('initialize');
      } else if (sensorEvent.eventName === 'activated') {
        this.emit('activate');
        this._command({cmdName: 'poll'}); // Start polling
      } else if (sensorEvent.eventName === 'deactivated') {
        this.emit('deactivate');
      } else if (sensorEvent.eventName === 'error') {
        log.warn('Received "error" from sensor service');
      } else if (sensorEvent.eventName === 'data') {
        /**
         * This event is emitted when sensor data is available.
         *
         * @event data
         * @property {number} sensorType One of the SENSOR_TYPE.* constants
         * @property {*[]} values: Sensor data as returned by the hardware sensor. The length and
         *           contents of the values array depends on which sensor type is being
         *           monitored. Please refer to `hardware/sensors.h` for details.
         * @memberof silk-sensors
         * @instance
         */
        this.emit('data', sensorEvent);
      } else {
        log.warn(`Error: Unknown sensors command ${line}`);
      }
    }
  }

  /**
   * @private
   */
  _command(cmd: Object) {
    if (!this._ready) {
      log.warn(`sensors service not ready, ignoring the command`);
      return;
    }
    // sensors socket expects the command data in the following format
    const event = JSON.stringify(cmd) + '\0';
    invariant(this._socket);
    this._socket.write(event);
    log.verbose(`sensors << ${JSON.stringify(cmd)}`);
  }
}

const sensors = new Sensors();
export default sensors;
<|endoftext|>


<commit_before>/**
 * @flow
 * @private
 */

import createLog from 'silk-log/device';
import Writable from 'readable-stream/writable';
import bindings from './speaker';

import type { WritableStreamOptions } from 'silk-streams';
import type { ConfigDeviceMic } from 'silk-config';
import type { SpeakerType } from './speaker';

type SpeakerOptions = WritableStreamOptions & ConfigDeviceMic;

const log = createLog('speaker');
const SAMPLES_PER_FRAME = 1024;
const GAIN_MIN = 0.0;
const GAIN_MAX = 1.0;

/**
 * This module provides a writable stream instance to stream raw PCM data to
 * the device speakers.
 * @module silk-speaker
 *
 * @example
 * 'use strict';
 *
 * const Speaker = require('silk-speaker').default;
 * const log = require('silk-alog');
 *
 * let speaker = new Speaker({ numChannels: 1,    // Default is 2
 *                             sampleRate: 16000, // Default is 44100
 *                             bytesPerSample: 2,
 *                             encoding: 'signed-integer'
 *                           });
 * speaker.setVolume(1.0);
 * speaker.write(pcmBuffer);
 * speaker.end();
 * speaker.on('close', () => log.info(`done`));
 */
export default class Speaker extends Writable {

  _speaker: SpeakerType;
  _closed: boolean = false;
  _opened: boolean = false;
  _options: SpeakerOptions;
  _chunkSize: number;

  constructor(options: SpeakerOptions) {
    super(options); // Calls the stream.Writable() constructor
    this._options = {
      numChannels: 2,
      sampleRate: 44100,
      bytesPerSample: 2,
      encoding: 'signed-integer',
      ...options,
    };

    if (null === this.getFormat()) {
      throw new Error('invalid PCM format specified');
    }

    // Calculate the block alignment
    let blockAlign = this._options.bytesPerSample / this._options.numChannels;
    this._chunkSize = blockAlign * SAMPLES_PER_FRAME;

    this.on('finish', () => this._flush());
    this._speaker = new bindings.Speaker();
  }

  /**
   * Returns the audio_format_t as defined in system/core/include/system/audio.h
   * @memberof silk-speaker
   * @instance
   * @return {number} audio format if valid or null otherwise
   */
  getFormat(): ?number {
    if (this._options.bytesPerSample === 1 &&
        this._options.encoding === 'unsigned-integer') {
      return bindings.AUDIO_FORMAT_PCM_8_BIT;
    }
    if (this._options.bytesPerSample === 2 &&
        this._options.encoding === 'signed-integer') {
      return bindings.AUDIO_FORMAT_PCM_16_BIT;
    }
    if (this._options.bytesPerSample === 4 &&
        this._options.encoding === 'float') {
      return bindings.AUDIO_FORMAT_PCM_FLOAT;
    }
    return null;
  }

  /**
   * Sets the specified output gain value on all channels of this track. Gain
   * values are clamped to the closed interval [0.0, 1.0]. A value of 0.0
   * results in zero gain (silence), and a value of 1.0 means signal unchanged.
   * The default value is 1.0.
   * @memberof silk-speaker
   * @instance
   *
   * @param gain output gain for all channels
   */
  setVolume(gain: number) {
    gain = this._clampGain(gain);
    this._speaker.setVolume(gain);
  }

  /**
   * @private
   */
  _clampGain(gainOrLevel: number) {
    if (isNaN(parseFloat(gainOrLevel))) {
      throw new Error(`${gainOrLevel} is not a valid floating point number`);
    }
    if (gainOrLevel < GAIN_MIN) {
      gainOrLevel = GAIN_MIN;
    } else if (gainOrLevel > GAIN_MAX) {
      gainOrLevel = GAIN_MAX;
    }
    return gainOrLevel;
  }

  /**
   * _write() callback for the Writable stream base class
   * @private
   */
  _write(chunk: Buffer, encoding: string, done: Function) {
    if (this._closed) {
      // close() has already been called. this should not be called
      done(new Error('write() call after close() call'));
      return;
    }

    // Open native bindings
    if (!this._opened) {
      this._speaker.open(this._options.numChannels, this._options.sampleRate,
          this.getFormat());
      this._opened = true;

      /**
       * This event is fired when speaker stream is opened and ready to receive
       * a stream to play
       *
       * @event open
       * @memberof silk-speaker
       * @instance
       */
      this.emit('open');
    }

    let buffer = chunk;
    let left = chunk;

    let write = () => {
      if (this._closed) {
        log.debug(`Aborting remainder of write() call since speaker is closed`);
        done();
        return;
      }
      if (!left) {
        done();
        return;
      }
      buffer = left;
      if (buffer.length > this._chunkSize) {
        let tmp = buffer;
        buffer = tmp.slice(0, this._chunkSize);
        left = tmp.slice(this._chunkSize);
      } else {
        left = null;
      }
      this._speaker.write(buffer, buffer.length, onwrite);
    };

    let onwrite = written => {
      if (written !== buffer.length) {
        done(new Error(`write() failed: ${written}`));
      } else if (left) {
        write();
      } else {
        done();
      }
    };

    write();
  }

  /**
   * Calls the `.close()` function on this Speaker instance.
   *
   * @private
   */
  _flush() {
    this.close();
  }

  /**
   * Closes the audio backend
   * @memberof silk-speaker
   * @instance
   */
  close() {
    log.debug(`Closing the stream`);
    if (this._closed) {
      log.debug('already closed...');
      return;
    }
    if (this._opened) {
      this._speaker.close();
      this._opened = false;
    }

    this._closed = true;

    /**
     * This event is fired after end() is called on the speaker and speaker is
     * done playing the entire audio stream. This speaker instance is essentially
     * finished after this point
     *
     * @event close
     * @memberof silk-speaker
     * @instance
     */
    this.emit('close');
  }
}
<commit_msg>Make silk-speaker config optional<commit_after>/**
 * @flow
 * @private
 */

import createLog from 'silk-log/device';
import Writable from 'readable-stream/writable';
import bindings from './speaker';

import type { WritableStreamOptions } from 'silk-streams';
import type { ConfigDeviceMic } from 'silk-config';
import type { SpeakerType } from './speaker';

type SpeakerOptions = WritableStreamOptions & ConfigDeviceMic;

const log = createLog('speaker');
const SAMPLES_PER_FRAME = 1024;
const GAIN_MIN = 0.0;
const GAIN_MAX = 1.0;

/**
 * This module provides a writable stream instance to stream raw PCM data to
 * the device speakers.
 * @module silk-speaker
 *
 * @example
 * 'use strict';
 *
 * const Speaker = require('silk-speaker').default;
 * const log = require('silk-alog');
 *
 * let speaker = new Speaker({ numChannels: 1,    // Default is 2
 *                             sampleRate: 16000, // Default is 44100
 *                             bytesPerSample: 2,
 *                             encoding: 'signed-integer'
 *                           });
 * speaker.setVolume(1.0);
 * speaker.write(pcmBuffer);
 * speaker.end();
 * speaker.on('close', () => log.info(`done`));
 */
export default class Speaker extends Writable {

  _speaker: SpeakerType;
  _closed: boolean = false;
  _opened: boolean = false;
  _options: SpeakerOptions;
  _chunkSize: number;

  constructor(options: ?SpeakerOptions) {
    super(options); // Calls the stream.Writable() constructor
    this._options = {
      numChannels: 2,
      sampleRate: 44100,
      bytesPerSample: 2,
      encoding: 'signed-integer',
      ...options,
    };

    if (null === this.getFormat()) {
      throw new Error('invalid PCM format specified');
    }

    // Calculate the block alignment
    let blockAlign = this._options.bytesPerSample / this._options.numChannels;
    this._chunkSize = blockAlign * SAMPLES_PER_FRAME;

    this.on('finish', () => this._flush());
    this._speaker = new bindings.Speaker();
  }

  /**
   * Returns the audio_format_t as defined in system/core/include/system/audio.h
   * @memberof silk-speaker
   * @instance
   * @return {number} audio format if valid or null otherwise
   */
  getFormat(): ?number {
    if (this._options.bytesPerSample === 1 &&
        this._options.encoding === 'unsigned-integer') {
      return bindings.AUDIO_FORMAT_PCM_8_BIT;
    }
    if (this._options.bytesPerSample === 2 &&
        this._options.encoding === 'signed-integer') {
      return bindings.AUDIO_FORMAT_PCM_16_BIT;
    }
    if (this._options.bytesPerSample === 4 &&
        this._options.encoding === 'float') {
      return bindings.AUDIO_FORMAT_PCM_FLOAT;
    }
    return null;
  }

  /**
   * Sets the specified output gain value on all channels of this track. Gain
   * values are clamped to the closed interval [0.0, 1.0]. A value of 0.0
   * results in zero gain (silence), and a value of 1.0 means signal unchanged.
   * The default value is 1.0.
   * @memberof silk-speaker
   * @instance
   *
   * @param gain output gain for all channels
   */
  setVolume(gain: number) {
    gain = this._clampGain(gain);
    this._speaker.setVolume(gain);
  }

  /**
   * @private
   */
  _clampGain(gainOrLevel: number) {
    if (isNaN(parseFloat(gainOrLevel))) {
      throw new Error(`${gainOrLevel} is not a valid floating point number`);
    }
    if (gainOrLevel < GAIN_MIN) {
      gainOrLevel = GAIN_MIN;
    } else if (gainOrLevel > GAIN_MAX) {
      gainOrLevel = GAIN_MAX;
    }
    return gainOrLevel;
  }

  /**
   * _write() callback for the Writable stream base class
   * @private
   */
  _write(chunk: Buffer, encoding: string, done: Function) {
    if (this._closed) {
      // close() has already been called. this should not be called
      done(new Error('write() call after close() call'));
      return;
    }

    // Open native bindings
    if (!this._opened) {
      this._speaker.open(this._options.numChannels, this._options.sampleRate,
          this.getFormat());
      this._opened = true;

      /**
       * This event is fired when speaker stream is opened and ready to receive
       * a stream to play
       *
       * @event open
       * @memberof silk-speaker
       * @instance
       */
      this.emit('open');
    }

    let buffer = chunk;
    let left = chunk;

    let write = () => {
      if (this._closed) {
        log.debug(`Aborting remainder of write() call since speaker is closed`);
        done();
        return;
      }
      if (!left) {
        done();
        return;
      }
      buffer = left;
      if (buffer.length > this._chunkSize) {
        let tmp = buffer;
        buffer = tmp.slice(0, this._chunkSize);
        left = tmp.slice(this._chunkSize);
      } else {
        left = null;
      }
      this._speaker.write(buffer, buffer.length, onwrite);
    };

    let onwrite = written => {
      if (written !== buffer.length) {
        done(new Error(`write() failed: ${written}`));
      } else if (left) {
        write();
      } else {
        done();
      }
    };

    write();
  }

  /**
   * Calls the `.close()` function on this Speaker instance.
   *
   * @private
   */
  _flush() {
    this.close();
  }

  /**
   * Closes the audio backend
   * @memberof silk-speaker
   * @instance
   */
  close() {
    log.debug(`Closing the stream`);
    if (this._closed) {
      log.debug('already closed...');
      return;
    }
    if (this._opened) {
      this._speaker.close();
      this._opened = false;
    }

    this._closed = true;

    /**
     * This event is fired after end() is called on the speaker and speaker is
     * done playing the entire audio stream. This speaker instance is essentially
     * finished after this point
     *
     * @event close
     * @memberof silk-speaker
     * @instance
     */
    this.emit('close');
  }
}
<|endoftext|>


<commit_before>var Viva = require('../../dist/vivagraph');
var gridSize = 100;
var layoutIterations = 100;

console.log('This is a very basic performance test of force directed layout');
console.log('Constructing graph: Grid ' + gridSize + 'x' + gridSize);
var generator = Viva.Graph.generator();
var graph = generator.grid(gridSize, gridSize);
console.log('Nodes: ' + graph.getNodesCount() + ', Edges: ' + graph.getLinksCount());

console.time('Layout time');
console.log('Performing ' + layoutIterations + ' iterations of layout');
var layout = Viva.Graph.Layout.forceDirected(graph);
for (var i = 0; i < layoutIterations; ++i) {
    layout.step();
}
console.timeEnd('Layout time');
console.log('Done.');
<commit_msg>Latest v8 is awesome. 35% performance improvement.<commit_after>// When node is compiled with the lates v8 version performance gain is 35% .. 40%
//
// Take a loook:
// V8 version: 3.20.2 (node version v0.11.5-pre+)
// Grid 100x100, Nodes: 10000, Edges: 19800, 100 layout iterations. Layout time: 9141ms
//
// V8 version: 3.14.5.9 (node version v0.10.10)
// Grid 100x100, Nodes: 10000, Edges: 19800, 100 layout iterations. Layout time: 14771ms

var Viva = require('../../dist/vivagraph');
var gridSize = 100;
var layoutIterations = 100;

console.log('This is a very basic performance test of force directed layout');
console.log('V8 version: ' + process.versions.v8);
var generator = Viva.Graph.generator();
var graph = generator.grid(gridSize, gridSize);
console.log('Grid ' + gridSize + 'x' + gridSize + ', Nodes: ' + graph.getNodesCount() + ', Edges: ' + graph.getLinksCount());

console.time('Layout time');
console.log('Performing ' + layoutIterations + ' iterations of layout');
var layout = Viva.Graph.Layout.forceDirected(graph);
for (var i = 0; i < layoutIterations; ++i) {
    layout.step();
}
console.timeEnd('Layout time');
console.log('Done.');

<|endoftext|>


<commit_before>define(function (require, exports, module) {
    "use strict";

    var StyleSheet = require("boost/StyleSheet");
    var LayoutPropTypes = require("boost/LayoutPropTypes");
    var validator = require("boost/validator");

    var number = validator.number;
    var dp = validator.dp;
    var string = validator.string;
    var color = validator.color;
    var font = validator.font;
    var _enum = validator.oneOf;

    var TextStylePropTypes = StyleSheet.createPropTypes(LayoutPropTypes, {
        "color": color,
        //"fontFamily": string,
        "backgroundColor": color, //
        "fontFamily": font,
        "fontSize": number,
        "fontStyle": _enum('normal', 'italic'),
        "fontWeight": _enum("normal", 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'),
        "letterSpacing": number,
        "lineHeight": dp,
        "textAlign": _enum("auto", 'left', 'right', 'center', 'justify'),
        //"textDecorationColor": string,
        //"textDecorationLine": _enum("none", 'underline', 'line-through', 'underline line-through'),
        //"textDecorationStyle": _enum("solid", 'double', 'dotted', 'dashed'),
        "writingDirection": _enum("auto", 'ltr', 'rtl')
    });

    module.exports = TextStylePropTypes;
});
<commit_msg>add text-decoration support<commit_after>define(function (require, exports, module) {
    "use strict";

    var StyleSheet = require("boost/StyleSheet");
    var LayoutPropTypes = require("boost/LayoutPropTypes");
    var validator = require("boost/validator");

    var number = validator.number;
    var dp = validator.dp;
    var string = validator.string;
    var color = validator.color;
    var font = validator.font;
    var _enum = validator.oneOf;

    var TextStylePropTypes = StyleSheet.createPropTypes(LayoutPropTypes, {
        "color": color,
        //"fontFamily": string,
        "backgroundColor": color, //
        "fontFamily": font,
        "fontSize": number,
        "fontStyle": _enum('normal', 'italic'),
        "fontWeight": _enum("normal", 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'),
        "letterSpacing": number,
        "lineHeight": dp,
        "textAlign": _enum("auto", 'left', 'right', 'center', 'justify'),
        //"textDecorationColor": string,
        //"textDecorationLine": _enum("none", 'underline', 'line-through', 'underline line-through'),
        //"textDecorationStyle": _enum("solid", 'double', 'dotted', 'dashed'),
        "writingDirection": _enum("auto", 'ltr', 'rtl'),
        "textDecoration": _enum("none", "underline", "line-through")
    });

    module.exports = TextStylePropTypes;
});
<|endoftext|>