<commit_before>(function(_) {
    'use strict';

    var api = window.grtpAPI || '//grtp.co/v1/';
    var gratipayURI = window.gratipayURI || 'https://gratipay.com/';
    var _grtp = window._grtp || [];

    window.Gratipay = window.Gratipay || {};

    Gratipay.users = {
        /**
         * @private
         * Request data for a user from a user's json endpoint
         *
         * @param {String} [service] Service name
         * @param {String} user User name
         * @param {String} endpoint JSON endpoint
         * @param {Function} callback(data) Callback with data from JSON endpoint
         */
        _get: function(service, user, endpoint, callback) {
            if (!callback) {
                callback = endpoint;
                endpoint = user;
                user = service;
                service = undefined;
            }

            var serviceURI = '';
            if (service) {
                serviceURI = 'on/' + service + '/';
            }

            // Use CORS XHR instead of JSONp if protocols match or we're being called from tests
            // Mostly so our mock Gratipay API works, but CORS is a bit cleaner regardless
            var mode = new RegExp('^(/|' + location.protocol + ')').test(gratipayURI) ? 'json' : 'jsonp';
            _[mode](gratipayURI + serviceURI + encodeURIComponent(user) + '/' + endpoint + '.json', callback);
        },

        // TODO: Add some sort of helper function so we aren't repeating the same
        //       6 lines of code with a single string changed for every endpoint.
        //       At the moment the duplicated code is just in are getPublic()
        //       and getCharts(), but it'll be required for every endpoint added.

        /**
         * Get a user's public.json
         *
         * @param {String} [service] Service name
         * @param {String} user User name
         * @param {Function} callback(data) Callback with data from user's public.json
         */
        getPublic: function(service, user, callback) {
            if (!callback) {
                callback = user;
                user = service;
                service = undefined;
            }

            this._get(service, user, 'public', callback);
        },

        /**
         * Get a user's charts.json
         * @param {String} [service] Service name
         * @param {String} user User name
         * @param {Function} callback(data) Callback with data from user's charts.json
         */
        getCharts: function(user, callback) {
            if (!callback) {
                callback = user;
                user = service;
                service = undefined;
            }

            this._get(user, 'charts', callback);
        }
    };

    Gratipay.widgets = {
        _: _,

        /**
         * Apply public.json data to a widget
         *
         * @param {Element} element Widget element
         * @param {Function} [cb(data)] Callback
         */
        apply: function(element, cb) {
            var username = element.getAttribute('data-gratipay-username') || element.getAttribute('data-gittip-username');

            function set(className, property, value, attribute, append) {
                var legacy_elements_array = Array.prototype.slice.call(element.getElementsByClassName('gittip-' + className));
                var elements_array = Array.prototype.slice.call(element.getElementsByClassName('gratipay-' + className));
                _.each(elements_array.concat(legacy_elements_array), function(element) {
                    if (attribute)
                        element.setAttribute(property, (append ? '' + element.getAttribute(property) : '') + value);
                    else
                        element[property] = value;
                });
            }

            function css(className, value) {
                set(className, 'style', ';' + value, true, true);
            }

            function text(className, value) {
                set(className, 'innerHTML', value);
            }

            function link(className, value) {
                if (!element.hasAttribute('target'))
                    set(className, 'target', '_top', true);

                set(className, 'href', value, true);
            }

            // Set up links and username before requesting public.json
            link('profile-link', gratipayURI + encodeURIComponent(username) + '/');
            link('link', gratipayURI);
            text('username', username);
            text('identity', 'I');

            Gratipay.users.getPublic(username, function(data) {
                element.setAttribute('data-gratipay-readystatus', 'ready');
                text('receiving', (data.receiving)? '$' + data.receiving : 'anonymously');
                text('username', data.username);
                text('my-tip-button', data.my_tip == 'self' ? 'You!' : (+data.my_tip > 0 ? data.my_tip : 'Gratipay'));
                text('goal', '$' + data.goal);
                text('giving', (data.giving)? '$' + data.giving : 'anonymously');
                link('profile-link', gratipayURI + encodeURIComponent(data.username) + '/');
                link('link', gratipayURI);
                css('goal-progress-bar', 'width:' + (data.receiving / data.goal * 100) + '%');

                if (data.number == 'plural')
                    text('identity', 'We');

                if (typeof cb == 'function')
                    cb(data);
            });
        },

        /**
         * Get the HTML for a widget
         *
         * @param {String} widget Widget name
         * @param {Function} cb(widget) Callback with widget element
         */
        getWidget: function(widget, cb) {
            _.get(api + 'widgets/' + widget + '.html', function(data) {
                // Fix for automatic scheme detection within injected <iframe>
                // elements -- where many browsers will interpret url(//grtp.co)
                // as url(about://grtp.co)
                data = data.replace(/url\((['"])?\/\//g, 'url($1' + location.protocol + '//');

                var widget = document.createElement('div');
                widget.innerHTML = data;
                cb(widget);
            });
        },

        /**
         * Load a widget using data from `element`
         *
         * @param {Element} element DOM Element
         */
        load: function(element) {
            var widgetType = element.getAttribute('data-gratipay-widget') || element.getAttribute('data-gittip-widget') || 'default';
            var username = element.getAttribute('data-gratipay-username') || element.getAttribute('data-gittip-username');

            // Set a readystate on the element
            element.setAttribute('data-gratipay-readystatus', 'loading');

            // Skip to applying widget data if this is a custom/user-defined widget
            if (['false', 'none', 'user-defined', 'custom'].indexOf(widgetType.toLowerCase()) != -1)
                return Gratipay.widgets.apply(element);

            // Get the widget (defined by widgetType)
            Gratipay.widgets.getWidget(widgetType, function(widget) {
                var container = _.jsonml(['iframe', {
                    style: 'border:0;margin:0;padding:0;width:0;height:0'
                }]);

                var style = _.jsoncss([
                    ['body', {
                        margin: 0,
                        padding: 0,
                        overflow: 'hidden'
                    }],
                    ['body > div', {
                        float: 'left'
                    }]
                ]);

                // FireFox's <iframe> gives a false-positive contentDocument.body
                // so we need to hook the onload event
                function onload() {
                    container.contentDocument.head.appendChild(style);
                    container.contentDocument.body.appendChild(widget);

                    // Resize the container <iframe> to fit the content
                    (function resize() {
                        container.style.width = widget.clientWidth + 'px';
                        container.style.height = widget.clientHeight + 'px';

                        setTimeout(resize, 500);
                    })();

                    Gratipay.widgets.apply(widget, function() {
                        element.setAttribute('data-gratipay-readystatus', 'ready');
                    });
                }

                // IE 8 requres .attachEvent(), everybody else can use .onload
                if (container.attachEvent)
                    container.attachEvent('onload', onload);
                else
                    container.onload = onload;

                widget.setAttribute('data-gratipay-username', username);
                element.parentNode.insertBefore(container, element);
            });
        },

        /**
         * Widget initialization
         */
        init: function() {
            _.each(_grtp, this.load);
        }
    };

    Gratipay.widgets.init();
})({
    // Helpers

    /**
     * Iterate over an array
     *
     * @param {Array} array Array to iterate over
     * @param {Function} fn(item, index, length) Callback function
     */
    each: function(array, fn) {
        'use strict';

        var length = array.length;
        for (var i=0; i<length; i++)
            fn(array[i], i, length);
    },

    /**
     * JSONCSS parser
     *
     * @param {Array} jsoncss JSONCSS selector list
     * @return {Element} `<style>` element
     */
    jsoncss: function(jsoncss) {
        'use strict';

        var _ = this;
        var css = '';

        _.each(jsoncss, function(selector) {
            if (typeof selector == 'string')
                return css += selector;

            css += selector.shift() + '{';

            _.each(selector, function(properties) {
                var value, property;
                for (property in properties) {
                    // Fix for automatic scheme detection within injected <iframe>
                    // elements -- where many browsers will interpret url(//grtp.co)
                    // as url(about://grtp.co)
                    value = properties[property]
                            .toString()
                            .replace(/url\((['"])?\/\//g, 'url($1' + location.protocol + '//');

                    css += property + ':' + value + ';';
                }
            });

            css += '}';
        });

        return this.jsonml(['style', css]);
    },

    /**
     * Simple JSONML parser
     * See: http://www.jsonml.org/
     *
     * @param {Array} jsonml JSONML element
     * @return {Element}
     */
    jsonml: function(jsonml) {
        'use strict';

        var _ = this;
        var node = document.createElement(jsonml.shift());

        _.each(jsonml, function(token) {
            if (typeof token === 'undefined')
                return;

            switch (token.constructor) {
                // Attributes to be applied to our node
                case Object:
                    for (var attribute in token)
                        node.setAttribute(attribute, token[attribute]);
                    break;

                // Child elements
                case Array:
                    node.appendChild(_.jsonml(token));
                    break;

                // Text nodes
                case String: case Number:
                    node.appendChild(document.createTextNode(token));
                    break;

                // Anything else should be considered a DOM Element
                default:
                    node.appendChild(token);
                    break;
            }
        });

        return node;
    },

    /**
     * Quick and dirty JSONp
     *
     * @param {String} url URL of JSON file
     * @param {Function} cb(json) Callback function
     */
    jsonp: function(url, cb) {
        'use strict';

        var _cb = '_req' + Math.floor(Math.random() * 1000);
        window[_cb] = function(_) { cb(_); delete window[_cb]; };
        document.head.appendChild(this.jsonml(['script', { src: url + '?callback=' + _cb }]));
    },

    // Private XHR wrapper
    _xhr: function(cb) {
        'use strict';

        var xhr = new XMLHttpRequest();

        // Use XDomainRequest if CORS via XMLHttpRequest is unavailable
        if (xhr.withCredentials === undefined && XDomainRequest)
            return this._xdr(cb);

        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4 && xhr.status == 200) cb();
        };

        return xhr;
    },

    // Private XDR wrapper
    _xdr: function(cb) {
        'use strict';

        var xdr = new XDomainRequest();
        xdr.onload = cb;
        return xdr;
    },

    /**
     * XHR wrapper
     *
     * @param {String} url URL of document to request
     * @param {Function} cb(data) Callback function
     */
    get: function(url, cb) {
        'use strict';

        var xhr = this._xhr(function() {
            cb(xhr.responseText);
        });

        xhr.open('GET', url);
        xhr.send();
    },

    /**
     * A little JSON
     *
     * @param {String} url URL of JSON file
     * @param {Function} cb(json) Callback function
     */
    json: function(url, cb) {
        'use strict';

        this.get(url, function(data) {
            cb(JSON.parse(data));
        });
    }

});
<commit_msg>Expose Gratipay as Gittip for legacy custom widgets<commit_after>(function(_) {
    'use strict';

    var api = window.grtpAPI || '//grtp.co/v1/';
    var gratipayURI = window.gratipayURI || 'https://gratipay.com/';
    var _grtp = window._grtp || [];

    // Define window.Gratipay if it hasn't previously been defined
    if (!window.Gratipay) {
        // For legacy reasons, have window.Gittip also be defined
        window.Gittip =
        window.Gratipay = {};
    }

    Gratipay.users = {
        /**
         * @private
         * Request data for a user from a user's json endpoint
         *
         * @param {String} [service] Service name
         * @param {String} user User name
         * @param {String} endpoint JSON endpoint
         * @param {Function} callback(data) Callback with data from JSON endpoint
         */
        _get: function(service, user, endpoint, callback) {
            if (!callback) {
                callback = endpoint;
                endpoint = user;
                user = service;
                service = undefined;
            }

            var serviceURI = '';
            if (service) {
                serviceURI = 'on/' + service + '/';
            }

            // Use CORS XHR instead of JSONp if protocols match or we're being called from tests
            // Mostly so our mock Gratipay API works, but CORS is a bit cleaner regardless
            var mode = new RegExp('^(/|' + location.protocol + ')').test(gratipayURI) ? 'json' : 'jsonp';
            _[mode](gratipayURI + serviceURI + encodeURIComponent(user) + '/' + endpoint + '.json', callback);
        },

        // TODO: Add some sort of helper function so we aren't repeating the same
        //       6 lines of code with a single string changed for every endpoint.
        //       At the moment the duplicated code is just in are getPublic()
        //       and getCharts(), but it'll be required for every endpoint added.

        /**
         * Get a user's public.json
         *
         * @param {String} [service] Service name
         * @param {String} user User name
         * @param {Function} callback(data) Callback with data from user's public.json
         */
        getPublic: function(service, user, callback) {
            if (!callback) {
                callback = user;
                user = service;
                service = undefined;
            }

            this._get(service, user, 'public', callback);
        },

        /**
         * Get a user's charts.json
         * @param {String} [service] Service name
         * @param {String} user User name
         * @param {Function} callback(data) Callback with data from user's charts.json
         */
        getCharts: function(user, callback) {
            if (!callback) {
                callback = user;
                user = service;
                service = undefined;
            }

            this._get(user, 'charts', callback);
        }
    };

    Gratipay.widgets = {
        _: _,

        /**
         * Apply public.json data to a widget
         *
         * @param {Element} element Widget element
         * @param {Function} [cb(data)] Callback
         */
        apply: function(element, cb) {
            var username = element.getAttribute('data-gratipay-username') || element.getAttribute('data-gittip-username');

            function set(className, property, value, attribute, append) {
                var legacy_elements_array = Array.prototype.slice.call(element.getElementsByClassName('gittip-' + className));
                var elements_array = Array.prototype.slice.call(element.getElementsByClassName('gratipay-' + className));
                _.each(elements_array.concat(legacy_elements_array), function(element) {
                    if (attribute)
                        element.setAttribute(property, (append ? '' + element.getAttribute(property) : '') + value);
                    else
                        element[property] = value;
                });
            }

            function css(className, value) {
                set(className, 'style', ';' + value, true, true);
            }

            function text(className, value) {
                set(className, 'innerHTML', value);
            }

            function link(className, value) {
                if (!element.hasAttribute('target'))
                    set(className, 'target', '_top', true);

                set(className, 'href', value, true);
            }

            // Set up links and username before requesting public.json
            link('profile-link', gratipayURI + encodeURIComponent(username) + '/');
            link('link', gratipayURI);
            text('username', username);
            text('identity', 'I');

            Gratipay.users.getPublic(username, function(data) {
                element.setAttribute('data-gratipay-readystatus', 'ready');
                text('receiving', (data.receiving)? '$' + data.receiving : 'anonymously');
                text('username', data.username);
                text('my-tip-button', data.my_tip == 'self' ? 'You!' : (+data.my_tip > 0 ? data.my_tip : 'Gratipay'));
                text('goal', '$' + data.goal);
                text('giving', (data.giving)? '$' + data.giving : 'anonymously');
                link('profile-link', gratipayURI + encodeURIComponent(data.username) + '/');
                link('link', gratipayURI);
                css('goal-progress-bar', 'width:' + (data.receiving / data.goal * 100) + '%');

                if (data.number == 'plural')
                    text('identity', 'We');

                if (typeof cb == 'function')
                    cb(data);
            });
        },

        /**
         * Get the HTML for a widget
         *
         * @param {String} widget Widget name
         * @param {Function} cb(widget) Callback with widget element
         */
        getWidget: function(widget, cb) {
            _.get(api + 'widgets/' + widget + '.html', function(data) {
                // Fix for automatic scheme detection within injected <iframe>
                // elements -- where many browsers will interpret url(//grtp.co)
                // as url(about://grtp.co)
                data = data.replace(/url\((['"])?\/\//g, 'url($1' + location.protocol + '//');

                var widget = document.createElement('div');
                widget.innerHTML = data;
                cb(widget);
            });
        },

        /**
         * Load a widget using data from `element`
         *
         * @param {Element} element DOM Element
         */
        load: function(element) {
            var widgetType = element.getAttribute('data-gratipay-widget') || element.getAttribute('data-gittip-widget') || 'default';
            var username = element.getAttribute('data-gratipay-username') || element.getAttribute('data-gittip-username');

            // Set a readystate on the element
            element.setAttribute('data-gratipay-readystatus', 'loading');

            // Skip to applying widget data if this is a custom/user-defined widget
            if (['false', 'none', 'user-defined', 'custom'].indexOf(widgetType.toLowerCase()) != -1)
                return Gratipay.widgets.apply(element);

            // Get the widget (defined by widgetType)
            Gratipay.widgets.getWidget(widgetType, function(widget) {
                var container = _.jsonml(['iframe', {
                    style: 'border:0;margin:0;padding:0;width:0;height:0'
                }]);

                var style = _.jsoncss([
                    ['body', {
                        margin: 0,
                        padding: 0,
                        overflow: 'hidden'
                    }],
                    ['body > div', {
                        float: 'left'
                    }]
                ]);

                // FireFox's <iframe> gives a false-positive contentDocument.body
                // so we need to hook the onload event
                function onload() {
                    container.contentDocument.head.appendChild(style);
                    container.contentDocument.body.appendChild(widget);

                    // Resize the container <iframe> to fit the content
                    (function resize() {
                        container.style.width = widget.clientWidth + 'px';
                        container.style.height = widget.clientHeight + 'px';

                        setTimeout(resize, 500);
                    })();

                    Gratipay.widgets.apply(widget, function() {
                        element.setAttribute('data-gratipay-readystatus', 'ready');
                    });
                }

                // IE 8 requres .attachEvent(), everybody else can use .onload
                if (container.attachEvent)
                    container.attachEvent('onload', onload);
                else
                    container.onload = onload;

                widget.setAttribute('data-gratipay-username', username);
                element.parentNode.insertBefore(container, element);
            });
        },

        /**
         * Widget initialization
         */
        init: function() {
            _.each(_grtp, this.load);
        }
    };

    Gratipay.widgets.init();
})({
    // Helpers

    /**
     * Iterate over an array
     *
     * @param {Array} array Array to iterate over
     * @param {Function} fn(item, index, length) Callback function
     */
    each: function(array, fn) {
        'use strict';

        var length = array.length;
        for (var i=0; i<length; i++)
            fn(array[i], i, length);
    },

    /**
     * JSONCSS parser
     *
     * @param {Array} jsoncss JSONCSS selector list
     * @return {Element} `<style>` element
     */
    jsoncss: function(jsoncss) {
        'use strict';

        var _ = this;
        var css = '';

        _.each(jsoncss, function(selector) {
            if (typeof selector == 'string')
                return css += selector;

            css += selector.shift() + '{';

            _.each(selector, function(properties) {
                var value, property;
                for (property in properties) {
                    // Fix for automatic scheme detection within injected <iframe>
                    // elements -- where many browsers will interpret url(//grtp.co)
                    // as url(about://grtp.co)
                    value = properties[property]
                            .toString()
                            .replace(/url\((['"])?\/\//g, 'url($1' + location.protocol + '//');

                    css += property + ':' + value + ';';
                }
            });

            css += '}';
        });

        return this.jsonml(['style', css]);
    },

    /**
     * Simple JSONML parser
     * See: http://www.jsonml.org/
     *
     * @param {Array} jsonml JSONML element
     * @return {Element}
     */
    jsonml: function(jsonml) {
        'use strict';

        var _ = this;
        var node = document.createElement(jsonml.shift());

        _.each(jsonml, function(token) {
            if (typeof token === 'undefined')
                return;

            switch (token.constructor) {
                // Attributes to be applied to our node
                case Object:
                    for (var attribute in token)
                        node.setAttribute(attribute, token[attribute]);
                    break;

                // Child elements
                case Array:
                    node.appendChild(_.jsonml(token));
                    break;

                // Text nodes
                case String: case Number:
                    node.appendChild(document.createTextNode(token));
                    break;

                // Anything else should be considered a DOM Element
                default:
                    node.appendChild(token);
                    break;
            }
        });

        return node;
    },

    /**
     * Quick and dirty JSONp
     *
     * @param {String} url URL of JSON file
     * @param {Function} cb(json) Callback function
     */
    jsonp: function(url, cb) {
        'use strict';

        var _cb = '_req' + Math.floor(Math.random() * 1000);
        window[_cb] = function(_) { cb(_); delete window[_cb]; };
        document.head.appendChild(this.jsonml(['script', { src: url + '?callback=' + _cb }]));
    },

    // Private XHR wrapper
    _xhr: function(cb) {
        'use strict';

        var xhr = new XMLHttpRequest();

        // Use XDomainRequest if CORS via XMLHttpRequest is unavailable
        if (xhr.withCredentials === undefined && XDomainRequest)
            return this._xdr(cb);

        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4 && xhr.status == 200) cb();
        };

        return xhr;
    },

    // Private XDR wrapper
    _xdr: function(cb) {
        'use strict';

        var xdr = new XDomainRequest();
        xdr.onload = cb;
        return xdr;
    },

    /**
     * XHR wrapper
     *
     * @param {String} url URL of document to request
     * @param {Function} cb(data) Callback function
     */
    get: function(url, cb) {
        'use strict';

        var xhr = this._xhr(function() {
            cb(xhr.responseText);
        });

        xhr.open('GET', url);
        xhr.send();
    },

    /**
     * A little JSON
     *
     * @param {String} url URL of JSON file
     * @param {Function} cb(json) Callback function
     */
    json: function(url, cb) {
        'use strict';

        this.get(url, function(data) {
            cb(JSON.parse(data));
        });
    }

});
<|endoftext|>


<commit_before>'use strict';

module.exports = IdObjRefMap;

var _ = require('lodash');
var orProxy = require('data/objrefProxy');
var utils = require('utils');

/**
 * Container for a collection of {@link GameObject}s (stored as
 * properties with their TSIDs as names), providing a `length`
 * property and an iterator helper function.
 *
 * @param {array} [data] optional initial content (array elements are
 *        shallow-copied into the map; objref descriptors are copied
 *        without loading the referenced objects)
 * @constructor
 */
function IdObjRefMap(data) {
	if (data && !_.isArray(data)) {
		throw new TypeError('invalid data type for IdObjRefMap: ' + typeof data);
	}
	for (var i = 0; data && i < data.length; i++) {
		var obj = data[i];
		if (obj.__isORP) {
			orProxy.setupObjRefProp(obj.tsid, this, obj.tsid);
		}
		else {
			// if the object is already loaded, we don't need the accessor
			// property (and this allows tests without persistence layer)
			this[obj.tsid] = obj;
		}
	}
}

/**
 * the number of stored objects
 *
 * @name length
 * @member {number}
 * @memberof IdObjRefMap
 * @instance
 */
Object.defineProperty(IdObjRefMap.prototype, 'length', {
	get: function get() {
		return Object.keys(this).length;
	},
});

/**
 * Iterates over objects in this map, optionally filtering by
 * `class_tsid`, and calls the given function on each one.
 *
 * @param {string} [classTsid] only iterate over objects of this class
 * @param {function} func function to be called for each (matching)
 *        object; signature: `func(obj)`
 */
IdObjRefMap.prototype.apiIterate = function apiIterate(classTsid, func) {
	// handle optional classTsid parameter
	if (_.isFunction(classTsid)) {
		func = classTsid;
		classTsid = undefined;
	}
	log.debug('IdObjRefMap.apiIterate(%s, %s)', classTsid, func.name);
	var keys = Object.keys(this);
	for (var i = 0; i < keys.length; i++) {
		var o = this[keys[i]];
		if (typeof o !== 'object') continue;
		if (!classTsid || o.class_tsid === classTsid) {
			func(o);
		}
	}
};
// hide function from for...in loops on IdObjRefMap instances:
utils.makeNonEnumerable(IdObjRefMap.prototype, 'apiIterate');
<commit_msg>Ignore null values in IdObjRefMap constructor<commit_after>'use strict';

module.exports = IdObjRefMap;

var _ = require('lodash');
var orProxy = require('data/objrefProxy');
var utils = require('utils');

/**
 * Container for a collection of {@link GameObject}s (stored as
 * properties with their TSIDs as names), providing a `length`
 * property and an iterator helper function.
 *
 * @param {array} [data] optional initial content (array elements are
 *        shallow-copied into the map; objref descriptors are copied
 *        without loading the referenced objects)
 * @constructor
 */
function IdObjRefMap(data) {
	if (data && !_.isArray(data)) {
		throw new TypeError('invalid data type for IdObjRefMap: ' + typeof data);
	}
	for (var i = 0; data && i < data.length; i++) {
		var obj = data[i];
		if (!_.isObject(obj)) {
			// ignore values that aren't objects
			continue;
		}
		if (obj.__isORP) {
			orProxy.setupObjRefProp(obj.tsid, this, obj.tsid);
		}
		else {
			// if the object is already loaded, we don't need the accessor
			// property (and this allows tests without persistence layer)
			this[obj.tsid] = obj;
		}
	}
}

/**
 * the number of stored objects
 *
 * @name length
 * @member {number}
 * @memberof IdObjRefMap
 * @instance
 */
Object.defineProperty(IdObjRefMap.prototype, 'length', {
	get: function get() {
		return Object.keys(this).length;
	},
});

/**
 * Iterates over objects in this map, optionally filtering by
 * `class_tsid`, and calls the given function on each one.
 *
 * @param {string} [classTsid] only iterate over objects of this class
 * @param {function} func function to be called for each (matching)
 *        object; signature: `func(obj)`
 */
IdObjRefMap.prototype.apiIterate = function apiIterate(classTsid, func) {
	// handle optional classTsid parameter
	if (_.isFunction(classTsid)) {
		func = classTsid;
		classTsid = undefined;
	}
	log.debug('IdObjRefMap.apiIterate(%s, %s)', classTsid, func.name);
	var keys = Object.keys(this);
	for (var i = 0; i < keys.length; i++) {
		var o = this[keys[i]];
		if (typeof o !== 'object') continue;
		if (!classTsid || o.class_tsid === classTsid) {
			func(o);
		}
	}
};
// hide function from for...in loops on IdObjRefMap instances:
utils.makeNonEnumerable(IdObjRefMap.prototype, 'apiIterate');
<|endoftext|>


<commit_before>'use strict';

var isPromise = require('./util/isPromise');

function values(object) {
    return Promise.all(
        Object.keys(object).map(function (key) {
            if (isPromise(object[key])) {
                return object[key].then(function (value) {
                    return { key: key, value: value };
                });
            }

            return { key: key, value: object[key] };
        })
    )
    .then(function (results) {
        // Zip the array of keys and values back to an object
        return results.reduce(function (values, object) {
            values[object.key] = object.value;

            return values;
        }, {});
    });
}

/*
 * Resolve the values of an object, whether they are promises or values, fulfilled.
 */
module.exports = function (fn) {
    if (typeof fn === 'function') {
        return function (object) {
            return values(object)
            .then(fn);
        };
    }

    // fn is an object
    return values(fn);
};
<commit_msg>Simplify values<commit_after>'use strict';

var isPromise = require('./util/isPromise');

function values(object) {
    var result = {};

    return Promise.all(
        Object.keys(object).map(function (key) {
            if (isPromise(object[key])) {
                return object[key].then(function (value) {
                    return (result[key] = value);
                });
            }

            result[key] = object[key];
        })
    )
    .then(function () { return result; });
}

/*
 * Resolve the values of an object, whether they are promises or values, fulfilled.
 */
module.exports = function (fn) {
    if (typeof fn === 'function') {
        return function (object) {
            return values(object)
            .then(fn);
        };
    }

    // fn is an object
    return values(fn);
};
<|endoftext|>


<commit_before>var _ = require('underscore');

module.exports = function verify(source, callback) {
    var s = source;
    var shardlevel = s._geocoder.shardlevel;

    function checkall(from, to, i, stats, callback) {
        stats = stats || [ 0, 0 ];

        if (i === Math.pow(16, shardlevel)) return callback(null, stats);

        s.getGeocoderData(from, i, function(err, buffer) {
            if (err) return callback(err);
            s._geocoder.load(buffer || new Buffer(0), from, i);
            var ids = s._geocoder.list(from, i);
            stats[0] += ids.length;
            (function check() {
                if (!ids.length) return checkall(from, to, ++i, stats, callback);
                var fromid = +ids.shift();
                var toids = s._geocoder.search(from, +i, fromid);
                if (from === 'degen') toids = toids.map(function(v) { return Math.floor(v/4); });
                stats[1] += toids.length;
                s._geocoder.getall(s.getGeocoderData.bind(s), to, toids, function(err, res) {
                    if (err) return callback(err);
                    if (!res.length) return callback(new Error('Broken reference: ' + [from,fromid,to,toids].join(',')));
                    process.nextTick(function() { check() });
                });
            })();
        });
    };

    var results = [];
    checkall('term', 'phrase', 0, null, function(err, stats) {
        if (err) return callback(err);
        results.push({ relation: ['term', 'phrase'], count: stats });
        checkall('term', 'grid', 0, null, function(err, stats) {
            if (err) return callback(err);
            results.push({ relation: ['term', 'grid'], count: stats });
            checkall('phrase', 'freq', 0, null, function(err, stats) {
                if (err) return callback(err);
                results.push({ relation: ['phrase', 'freq'], count: stats });
                return callback(null, results);
            });
        });
    });
}
<commit_msg>call cxx function directly by new name<commit_after>var _ = require('underscore');

module.exports = function verify(source, callback) {
    var s = source;
    var shardlevel = s._geocoder.shardlevel;

    function checkall(from, to, i, stats, callback) {
        stats = stats || [ 0, 0 ];

        if (i === Math.pow(16, shardlevel)) return callback(null, stats);

        s.getGeocoderData(from, i, function(err, buffer) {
            if (err) return callback(err);
            s._geocoder.load(buffer || new Buffer(0), from, i);
            var ids = s._geocoder.list(from, i);
            stats[0] += ids.length;
            (function check() {
                if (!ids.length) return checkall(from, to, ++i, stats, callback);
                var fromid = +ids.shift();
                var toids = s._geocoder._get(from, +i, fromid);
                if (from === 'degen') toids = toids.map(function(v) { return Math.floor(v/4); });
                stats[1] += toids.length;
                s._geocoder.getall(s.getGeocoderData.bind(s), to, toids, function(err, res) {
                    if (err) return callback(err);
                    if (!res.length) return callback(new Error('Broken reference: ' + [from,fromid,to,toids].join(',')));
                    process.nextTick(function() { check() });
                });
            })();
        });
    };

    var results = [];
    checkall('term', 'phrase', 0, null, function(err, stats) {
        if (err) return callback(err);
        results.push({ relation: ['term', 'phrase'], count: stats });
        checkall('term', 'grid', 0, null, function(err, stats) {
            if (err) return callback(err);
            results.push({ relation: ['term', 'grid'], count: stats });
            checkall('phrase', 'freq', 0, null, function(err, stats) {
                if (err) return callback(err);
                results.push({ relation: ['phrase', 'freq'], count: stats });
                return callback(null, results);
            });
        });
    });
}
<|endoftext|>


<commit_before>import Immutable from 'immutable'

import Model from './ModelInfo'

export class Constraint extends Immutable.Record({
  _model: new Model({
    name: 'constraint.constraint.models',
    version: '0.1.0'
  }),
  name: null,
  value: null,
  expression: () => { return false }
}) {
  evaluate(d) {
    return this.get('expression')(d)
  }

  toJSONSchema() {
    const obj = {}
    const key = this.get('name')
    const value = this.get('value')
    obj[key] = value
    return obj
  }
}

export class MultipleOfConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'multiple-of.constraint.models',
        version: '0.1.0'
      }),
      name: 'multipleOf',
      value: value,
      expression: d => {
        return d % value === 0
      }
    }
    super(obj)
  }
}

export class MaximumConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'maximum.constraint.models',
        version: '0.1.0'
      }),
      name: 'maximum',
      value: value,
      expression: d => {
        return d <= value
      }
    }
    super(obj)
  }
}

export class ExclusiveMaximumConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'exclusive-maximum.constraint.models',
        version: '0.1.0'
      }),
      name: 'exclusiveMaximum',
      value: value,
      expression: d => {
        return d < value
      }
    }
    super(obj)
  }

  toJSONSchema() {
    const obj = {}
    const key = this.get('name')
    const value = this.get('value')
    obj.maximum = value
    obj[key] = true
    return obj
  }
}

export class MinimumConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'minimum.constraint.models',
        version: '0.1.0'
      }),
      name: 'minimum',
      value: value,
      expression: d => {
        return d >= value
      }
    }
    super(obj)
  }
}

export class ExclusiveMinimumConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'exclusive-minimum.constraint.models',
        version: '0.1.0'
      }),
      name: 'exclusiveMinimum',
      value: value,
      expression: d => {
        return d > value
      }
    }
    super(obj)
  }

  toJSONSchema() {
    const obj = {}
    const key = this.get('name')
    const value = this.get('value')
    obj.minimum = value
    obj[key] = true
    return obj
  }
}

export class MaximumLengthConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'maximum-length.constraint.models',
        version: '0.1.0'
      }),
      name: 'maxLength',
      value: value,
      expression: d => {
        return d.length <= value
      }
    }
    super(obj)
  }
}

export class MinimumLengthConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'minimum-length.constraint.models',
        version: '0.1.0'
      }),
      name: 'minLength',
      value: value,
      expression: d => {
        return d.length >= value
      }
    }
    super(obj)
  }
}

export class PatternConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'pattern.constraint.models',
        version: '0.1.0'
      }),
      name: 'pattern',
      value: value,
      expression: d => {
        return d.match(value) !== null
      }
    }
    super(obj)
  }
}

export class MaximumItemsConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'maximum-items.constraint.models',
        version: '0.1.0'
      }),
      name: 'maxItems',
      value: value,
      expression: d => {
        if (typeof value === 'undefined' || value === null) {
          return true
        }
        return (d.length || d.size) <= value
      }
    }
    super(obj)
  }
}

export class MinimumItemsConstraint extends Constraint {
  constructor(value = 0) {
    const obj = {
      _model: new Model({
        name: 'minimum-items.constraint.models',
        version: '0.1.0'
      }),
      name: 'minItems',
      value: value,
      expression: d => {
        return (d.length || d.size) >= value
      }
    }
    super(obj)
  }
}

export class UniqueItemsConstraint extends Constraint {
  constructor(value = false) {
    const obj = {
      _model: new Model({
        name: 'unique-items.constraint.models',
        version: '0.1.0'
      }),
      name: 'uniqueItems',
      value: value,
      expression: d => {
        if (!value) {
          return true
        }
        const valueSet = d.reduce((_obj, item) => {
          const itemKey = JSON.stringify(item)
          _obj[itemKey] = true
          return _obj
        }, {})
        return (d.length || d.size) === Object.keys(valueSet).length
      }
    }
    super(obj)
  }
}

export class MaximumPropertiesConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'maximum-properties.constraint.models',
        version: '0.1.0'
      }),
      name: 'maxProperties',
      value: value,
      expression: d => {
        if (typeof value === 'undefined' || value === null) {
          return true
        }
        return Object.keys(d).length <= value
      }
    }
    super(obj)
  }
}

export class MinimumPropertiesConstraint extends Constraint {
  constructor(value = 0) {
    const obj = {
      _model: new Model({
        name: 'minimum-properties.constraint.models',
        version: '0.1.0'
      }),
      name: 'minProperties',
      value: value,
      expression: d => {
        return Object.keys(d).length >= value
      }
    }
    super(obj)
  }
}

export class EnumConstraint extends Constraint {
  constructor(value = []) {
    const obj = {
      _model: new Model({
        name: 'enum.constraint.models',
        version: '0.1.0'
      }),
      name: 'enum',
      value: value,
      expression: d => {
        return value.indexOf(d) >= 0
      }
    }
    super(obj)
  }
}

const _Constraint = {
  Constraint: Constraint,
  MultipleOf: MultipleOfConstraint,
  Maximum: MaximumConstraint,
  ExclusiveMaximum: ExclusiveMaximumConstraint,
  Minimum: MinimumConstraint,
  ExclusiveMinimum: ExclusiveMinimumConstraint,
  MaximumLength: MaximumLengthConstraint,
  MinimumLength: MinimumLengthConstraint,
  Pattern: PatternConstraint,
  MaximumItems: MaximumItemsConstraint,
  MinimumItems: MinimumItemsConstraint,
  UniqueItems: UniqueItemsConstraint,
  MaximumProperties: MaximumPropertiesConstraint,
  MinimumProperties: MinimumPropertiesConstraint,
  Enum: EnumConstraint
}

export default _Constraint
<commit_msg>documented Constraint.js<commit_after>import Immutable from 'immutable'

import Model from './ModelInfo'

/**
 * Metadata about the Constraint Record.
 * Used for internal serialization and deserialization
 */
const modelInstance = {
  name: 'constraint.constraint.models',
  version: '0.1.0'
}
const model = new Model(modelInstance)

/**
 * Default Spec for the BasicAuth Record.
 * - `name` is the name of the Constraint
 * - `value` is the context used by the `expression` field to validate an object
 * - `expression` is used to test whether an object is valid or not
 */
const ConstraintSpec = {
  _model: model,
  name: null,
  value: null,
  expression: () => { return false }
}

/**
 * The base Constraint class
 */
export class Constraint extends Immutable.Record(ConstraintSpec) {
  evaluate(d) {
    return this.get('expression')(d)
  }

  toJSONSchema() {
    const obj = {}
    const key = this.get('name')
    const value = this.get('value')
    obj[key] = value
    return obj
  }
}

/**
 * A MultipleOf Constraint.
 * evaluate returns true if and only if the object to test is a multiple of
 * the value passed to the constructor
 */
export class MultipleOfConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'multiple-of.constraint.models',
        version: '0.1.0'
      }),
      name: 'multipleOf',
      value: value,
      expression: d => {
        return d % value === 0
      }
    }
    super(obj)
  }
}

/**
 * A Maximum Constraint.
 * evaluate returns true if and only if the object to test is smaller than
 * the value passed to the constructor
 */
export class MaximumConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'maximum.constraint.models',
        version: '0.1.0'
      }),
      name: 'maximum',
      value: value,
      expression: d => {
        return d <= value
      }
    }
    super(obj)
  }
}

/**
 * An ExclusiveMaximum Constraint.
 * evaluate returns true if and only if the object to test is strictly smaller
 * the value passed to the constructor
 */
export class ExclusiveMaximumConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'exclusive-maximum.constraint.models',
        version: '0.1.0'
      }),
      name: 'exclusiveMaximum',
      value: value,
      expression: d => {
        return d < value
      }
    }
    super(obj)
  }

  toJSONSchema() {
    const obj = {}
    const key = this.get('name')
    const value = this.get('value')
    obj.maximum = value
    obj[key] = true
    return obj
  }
}

/**
 * A Minimum Constraint
 * evaluate returns true if and only if the object to test is larger than
 * the value passed to the constructor
 */
export class MinimumConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'minimum.constraint.models',
        version: '0.1.0'
      }),
      name: 'minimum',
      value: value,
      expression: d => {
        return d >= value
      }
    }
    super(obj)
  }
}

/**
 * An ExclusiveMinimum Constraint.
 * evaluate returns true if and only if the object to test is strictly larger than
 * the value passed to the constructor
 */
export class ExclusiveMinimumConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'exclusive-minimum.constraint.models',
        version: '0.1.0'
      }),
      name: 'exclusiveMinimum',
      value: value,
      expression: d => {
        return d > value
      }
    }
    super(obj)
  }

  toJSONSchema() {
    const obj = {}
    const key = this.get('name')
    const value = this.get('value')
    obj.minimum = value
    obj[key] = true
    return obj
  }
}

/**
 * A MaximumLength Constraint.
 * evaluate returns true if and only if the object to test has a length smaller than
 * the value passed to the constructor
 */
export class MaximumLengthConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'maximum-length.constraint.models',
        version: '0.1.0'
      }),
      name: 'maxLength',
      value: value,
      expression: d => {
        return d.length <= value
      }
    }
    super(obj)
  }
}

/**
 * A MinimumLength Constraint.
 * evaluate returns true if and only if the object to test has a length larger than
 * the value passed to the constructor
 */
export class MinimumLengthConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'minimum-length.constraint.models',
        version: '0.1.0'
      }),
      name: 'minLength',
      value: value,
      expression: d => {
        return d.length >= value
      }
    }
    super(obj)
  }
}

/**
 * A Pattern Constraint.
 * evaluate returns true if and only if the object to test matches
 * the pattern passed to the constructor
 */
export class PatternConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'pattern.constraint.models',
        version: '0.1.0'
      }),
      name: 'pattern',
      value: value,
      expression: d => {
        return d.match(value) !== null
      }
    }
    super(obj)
  }
}

/**
 * A MaximumItems Constraint.
 * evaluate returns true if and only if the object to test has less items than
 * the value passed to the constructor
 */
export class MaximumItemsConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'maximum-items.constraint.models',
        version: '0.1.0'
      }),
      name: 'maxItems',
      value: value,
      expression: d => {
        if (typeof value === 'undefined' || value === null) {
          return true
        }
        return (d.length || d.size) <= value
      }
    }
    super(obj)
  }
}

/**
 * A MinimumItems Constraint.
 * evaluate returns true if and only if the object to test has more items than
 * the value passed to the constructor
 */
export class MinimumItemsConstraint extends Constraint {
  constructor(value = 0) {
    const obj = {
      _model: new Model({
        name: 'minimum-items.constraint.models',
        version: '0.1.0'
      }),
      name: 'minItems',
      value: value,
      expression: d => {
        return (d.length || d.size) >= value
      }
    }
    super(obj)
  }
}

/**
 * A UniqueItems Constraint.
 * evaluate returns true if and only if the object to test contains only
 * unique values
 */
export class UniqueItemsConstraint extends Constraint {
  constructor(value = false) {
    const obj = {
      _model: new Model({
        name: 'unique-items.constraint.models',
        version: '0.1.0'
      }),
      name: 'uniqueItems',
      value: value,
      expression: d => {
        if (!value) {
          return true
        }
        const valueSet = d.reduce((_obj, item) => {
          const itemKey = JSON.stringify(item)
          _obj[itemKey] = true
          return _obj
        }, {})
        return (d.length || d.size) === Object.keys(valueSet).length
      }
    }
    super(obj)
  }
}

/**
 * A MaximumProperties Constraint.
 * evaluate returns true if and only if the object to test has less properties than
 * the value passed to the constructor
 */
export class MaximumPropertiesConstraint extends Constraint {
  constructor(value) {
    const obj = {
      _model: new Model({
        name: 'maximum-properties.constraint.models',
        version: '0.1.0'
      }),
      name: 'maxProperties',
      value: value,
      expression: d => {
        if (typeof value === 'undefined' || value === null) {
          return true
        }
        return Object.keys(d).length <= value
      }
    }
    super(obj)
  }
}

/**
 * A MinimumProperties Constraint.
 * evaluate returns true if and only if the object to test has more properties than
 * the value passed to the constructor
 */
export class MinimumPropertiesConstraint extends Constraint {
  constructor(value = 0) {
    const obj = {
      _model: new Model({
        name: 'minimum-properties.constraint.models',
        version: '0.1.0'
      }),
      name: 'minProperties',
      value: value,
      expression: d => {
        return Object.keys(d).length >= value
      }
    }
    super(obj)
  }
}

/**
 * An Enum Constraint.
 * evaluate returns true if and only if the object to test is in
 * the list of values passed to the constructor
 */
export class EnumConstraint extends Constraint {
  constructor(value = []) {
    const obj = {
      _model: new Model({
        name: 'enum.constraint.models',
        version: '0.1.0'
      }),
      name: 'enum',
      value: value,
      expression: d => {
        return value.indexOf(d) >= 0
      }
    }
    super(obj)
  }
}

const _Constraint = {
  Constraint: Constraint,
  MultipleOf: MultipleOfConstraint,
  Maximum: MaximumConstraint,
  ExclusiveMaximum: ExclusiveMaximumConstraint,
  Minimum: MinimumConstraint,
  ExclusiveMinimum: ExclusiveMinimumConstraint,
  MaximumLength: MaximumLengthConstraint,
  MinimumLength: MinimumLengthConstraint,
  Pattern: PatternConstraint,
  MaximumItems: MaximumItemsConstraint,
  MinimumItems: MinimumItemsConstraint,
  UniqueItems: UniqueItemsConstraint,
  MaximumProperties: MaximumPropertiesConstraint,
  MinimumProperties: MinimumPropertiesConstraint,
  Enum: EnumConstraint
}

export default _Constraint
<|endoftext|>


<commit_before>const _ = require('lodash/fp')
const mem = require('mem')
const HKDF = require('node-hkdf-sync')

const configManager = require('./config-manager')
const pify = require('pify')
const fs = pify(require('fs'))

const options = require('./options')
const ph = require('./plugin-helper')
const logger = require('./logger')

const FETCH_INTERVAL = 5000
const INSUFFICIENT_FUNDS_CODE = 570
const INSUFFICIENT_FUNDS_NAME = 'InsufficientFunds'
const ZERO_CONF_EXPIRATION = 60000

function httpError (msg, code) {
  const err = new Error(msg)
  err.name = 'HTTPError'
  err.code = code || 500

  return err
}

function computeSeed (masterSeed) {
  const hkdf = new HKDF('sha256', 'lamassu-server-salt', masterSeed)
  return hkdf.derive('wallet-seed', 32)
}

function fetchWallet (settings, cryptoCode) {
  return fs.readFile(options.seedPath, 'utf8')
    .then(hex => {
      const masterSeed = Buffer.from(hex.trim(), 'hex')
      const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
      const wallet = ph.load(ph.WALLET, plugin)
      const account = settings.accounts[plugin]

      return {wallet, account: _.set('seed', computeSeed(masterSeed), account)}
    })
}

const lastBalance = {}

function _balance (settings, cryptoCode) {
  logger.debug('Polled wallet balance')
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.balance(r.account, cryptoCode))
    .then(balance => ({balance, timestamp: Date.now()}))
    .then(r => {
      lastBalance[cryptoCode] = r
      return r
    })
    .catch(err => {
      console.error(err)
      return lastBalance[cryptoCode]
    })
}

function sendCoins (settings, toAddress, cryptoAtoms, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => {
      return r.wallet.sendCoins(r.account, toAddress, cryptoAtoms, cryptoCode)
        .then(res => {
          mem.clear(module.exports.balance)
          return res
        })
    })
    .catch(err => {
      if (err.name === INSUFFICIENT_FUNDS_NAME) {
        throw httpError(INSUFFICIENT_FUNDS_NAME, INSUFFICIENT_FUNDS_CODE)
      }

      throw err
    })
}

function newAddress (settings, info) {
  return fetchWallet(settings, info.cryptoCode)
    .then(r => r.wallet.newAddress(r.account, info))
}

function newFunding (settings, cryptoCode, address) {
  return fetchWallet(settings, cryptoCode)
    .then(r => {
      const wallet = r.wallet
      const account = r.account

      return wallet.newFunding(account, cryptoCode)
    })
}

function getWalletStatus (settings, tx) {
  return fetchWallet(settings, tx.cryptoCode)
    .then(r => r.wallet.getStatus(r.account, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode))
}

function authorizeZeroConf (settings, tx, machineId) {
  const cryptoConfig = configManager.cryptoScoped(tx.cryptoCode, settings.config)
  const machineConfig = configManager.machineScoped(machineId, settings.config)
  const plugin = cryptoConfig.zeroConf
  const zeroConfLimit = machineConfig.zeroConfLimit

  if (plugin === 'no-zero-conf' || tx.fiat.gt(zeroConfLimit)) {
    return Promise.resolve(false)
  }

  if (plugin === 'all-zero-conf') return Promise.resolve(true)

  const zeroConf = ph.load(ph.ZERO_CONF, plugin)
  const account = settings.accounts[plugin]

  return zeroConf.authorize(account, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)
}

function getStatus (settings, tx, machineId) {
  return getWalletStatus(settings, tx)
    .then((statusRec) => {
      if (statusRec.status === 'authorized') {
        return authorizeZeroConf(settings, tx, machineId)
          .then(isAuthorized => {
            const publishAge = Date.now() - tx.publishedAt

            const unauthorizedStatus = publishAge < ZERO_CONF_EXPIRATION
              ? 'published'
              : 'rejected'

            const status = isAuthorized ? 'authorized' : unauthorizedStatus

            return {status}
          })
      }

      return statusRec
    })
}

function sweep (settings, cryptoCode, hdIndex) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.sweep(r.account, cryptoCode, hdIndex))
}

function isHd (settings, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.supportsHd)
}

function cryptoNetwork (settings, cryptoCode) {
  const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
  const wallet = ph.load(ph.WALLET, plugin)
  const account = settings.accounts[plugin]

  if (!wallet.cryptoNetwork) return Promise.resolve(false)
  return wallet.cryptoNetwork(account, cryptoCode)
}

const balance = mem(_balance, {
  maxAge: FETCH_INTERVAL,
  cacheKey: (settings, cryptoCode) => cryptoCode
})

module.exports = {
  balance,
  sendCoins,
  newAddress,
  getStatus,
  sweep,
  isHd,
  newFunding,
  cryptoNetwork
}
<commit_msg>remove debug<commit_after>const _ = require('lodash/fp')
const mem = require('mem')
const HKDF = require('node-hkdf-sync')

const configManager = require('./config-manager')
const pify = require('pify')
const fs = pify(require('fs'))

const options = require('./options')
const ph = require('./plugin-helper')
const logger = require('./logger')

const FETCH_INTERVAL = 5000
const INSUFFICIENT_FUNDS_CODE = 570
const INSUFFICIENT_FUNDS_NAME = 'InsufficientFunds'
const ZERO_CONF_EXPIRATION = 60000

function httpError (msg, code) {
  const err = new Error(msg)
  err.name = 'HTTPError'
  err.code = code || 500

  return err
}

function computeSeed (masterSeed) {
  const hkdf = new HKDF('sha256', 'lamassu-server-salt', masterSeed)
  return hkdf.derive('wallet-seed', 32)
}

function fetchWallet (settings, cryptoCode) {
  return fs.readFile(options.seedPath, 'utf8')
    .then(hex => {
      const masterSeed = Buffer.from(hex.trim(), 'hex')
      const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
      const wallet = ph.load(ph.WALLET, plugin)
      const account = settings.accounts[plugin]

      return {wallet, account: _.set('seed', computeSeed(masterSeed), account)}
    })
}

const lastBalance = {}

function _balance (settings, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.balance(r.account, cryptoCode))
    .then(balance => ({balance, timestamp: Date.now()}))
    .then(r => {
      lastBalance[cryptoCode] = r
      return r
    })
    .catch(err => {
      console.error(err)
      return lastBalance[cryptoCode]
    })
}

function sendCoins (settings, toAddress, cryptoAtoms, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => {
      return r.wallet.sendCoins(r.account, toAddress, cryptoAtoms, cryptoCode)
        .then(res => {
          mem.clear(module.exports.balance)
          return res
        })
    })
    .catch(err => {
      if (err.name === INSUFFICIENT_FUNDS_NAME) {
        throw httpError(INSUFFICIENT_FUNDS_NAME, INSUFFICIENT_FUNDS_CODE)
      }

      throw err
    })
}

function newAddress (settings, info) {
  return fetchWallet(settings, info.cryptoCode)
    .then(r => r.wallet.newAddress(r.account, info))
}

function newFunding (settings, cryptoCode, address) {
  return fetchWallet(settings, cryptoCode)
    .then(r => {
      const wallet = r.wallet
      const account = r.account

      return wallet.newFunding(account, cryptoCode)
    })
}

function getWalletStatus (settings, tx) {
  return fetchWallet(settings, tx.cryptoCode)
    .then(r => r.wallet.getStatus(r.account, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode))
}

function authorizeZeroConf (settings, tx, machineId) {
  const cryptoConfig = configManager.cryptoScoped(tx.cryptoCode, settings.config)
  const machineConfig = configManager.machineScoped(machineId, settings.config)
  const plugin = cryptoConfig.zeroConf
  const zeroConfLimit = machineConfig.zeroConfLimit

  if (plugin === 'no-zero-conf' || tx.fiat.gt(zeroConfLimit)) {
    return Promise.resolve(false)
  }

  if (plugin === 'all-zero-conf') return Promise.resolve(true)

  const zeroConf = ph.load(ph.ZERO_CONF, plugin)
  const account = settings.accounts[plugin]

  return zeroConf.authorize(account, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)
}

function getStatus (settings, tx, machineId) {
  return getWalletStatus(settings, tx)
    .then((statusRec) => {
      if (statusRec.status === 'authorized') {
        return authorizeZeroConf(settings, tx, machineId)
          .then(isAuthorized => {
            const publishAge = Date.now() - tx.publishedAt

            const unauthorizedStatus = publishAge < ZERO_CONF_EXPIRATION
              ? 'published'
              : 'rejected'

            const status = isAuthorized ? 'authorized' : unauthorizedStatus

            return {status}
          })
      }

      return statusRec
    })
}

function sweep (settings, cryptoCode, hdIndex) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.sweep(r.account, cryptoCode, hdIndex))
}

function isHd (settings, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.supportsHd)
}

function cryptoNetwork (settings, cryptoCode) {
  const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
  const wallet = ph.load(ph.WALLET, plugin)
  const account = settings.accounts[plugin]

  if (!wallet.cryptoNetwork) return Promise.resolve(false)
  return wallet.cryptoNetwork(account, cryptoCode)
}

const balance = mem(_balance, {
  maxAge: FETCH_INTERVAL,
  cacheKey: (settings, cryptoCode) => cryptoCode
})

module.exports = {
  balance,
  sendCoins,
  newAddress,
  getStatus,
  sweep,
  isHd,
  newFunding,
  cryptoNetwork
}
<|endoftext|>


<commit_before>// @flow

/* Wraps glamor's stylesheet and exports a singleton for the rest
*  of the app to use. */

import { StyleSheet as GlamorStyle } from '../vendor/glamor/sheet'

class StyleSheet {
  styleSheet: GlamorStyle
  constructor() {
    this.styleSheet = new GlamorStyle({ speedy: false, maxLength: 40 })
  }
  get injected(): boolean {
    return this.styleSheet.injected
  }
  inject() {
    return this.styleSheet.inject()
  }
  insert(css: string) {
    return this.styleSheet.insert(css)
  }
  reset() {
    if (this.styleSheet.sheet) this.styleSheet.flush()
  }
  getCSS({ min = true }: { min: boolean } = {}) {
    return this.styleSheet.rules().map(rule => rule.cssText).join(min ? '' : '\n')
  }
}

export default new StyleSheet()
<commit_msg>fix issue in v2 and not in vendor<commit_after>// @flow

/* Wraps glamor's stylesheet and exports a singleton for the rest
*  of the app to use. */

import { StyleSheet as GlamorStyle } from '../vendor/glamor/sheet'

class StyleSheet {
  styleSheet: GlamorStyle
  constructor() {
    this.styleSheet = new GlamorStyle({ speedy: false, maxLength: 40 })
  }
  get injected(): boolean {
    return this.styleSheet.injected
  }
  inject() {
    return this.styleSheet.inject()
  }
  insert(css: string) {
    return this.styleSheet.insert(css)
  }
  reset() {
    if (this.styleSheet.sheet) this.styleSheet.flush()
  }
  getCSS({ min = true }: { min: boolean } = {}) {
    return this.styleSheet.sheet ?
      this.styleSheet.rules().map(rule => rule.cssText).join(min ? '' : '\n')
      : '';
  }
}

export default new StyleSheet()
<|endoftext|>


<commit_before>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/**
 * A class to hold information about the Codewalk Viewer.
 * @param {jQuery} context The top element in whose context the viewer should
 *     operate.  It will not touch any elements above this one.
 * @constructor
 */
 var CodewalkViewer = function(context) {
  this.context = context;

  /**
   * The div that contains all of the comments and their controls.
   */
  this.commentColumn = this.context.find('#comment-column');

  /**
   * The div that contains the comments proper.
   */
  this.commentArea = this.context.find('#comment-area');

  /**
   * The div that wraps the iframe with the code, as well as the drop down menu
   * listing the different files.
   * @type {jQuery}
   */
  this.codeColumn = this.context.find('#code-column');

  /**
   * The div that contains the code but excludes the options strip.
   * @type {jQuery}
   */
  this.codeArea = this.context.find('#code-area');

  /**
   * The iframe that holds the code (from Sourcerer).
   * @type {jQuery}
   */
  this.codeDisplay = this.context.find('#code-display');

  /**
   * The overlaid div used as a grab handle for sizing the code/comment panes.
   * @type {jQuery}
   */
  this.sizer = this.context.find('#sizer');

  /**
   * The full-screen overlay that ensures we don't lose track of the mouse
   * while dragging.
   * @type {jQuery}
   */
  this.overlay = this.context.find('#overlay');

  /**
   * The hidden input field that we use to hold the focus so that we can detect
   * shortcut keypresses.
   * @type {jQuery}
   */
  this.shortcutInput = this.context.find('#shortcut-input');

  /**
   * The last comment that was selected.
   * @type {jQuery}
   */
  this.lastSelected = null;
};

/**
 * Minimum width of the comments or code pane, in pixels.
 * @type {number}
 */
CodewalkViewer.MIN_PANE_WIDTH = 200;

/**
 * Navigate the code iframe to the given url and update the code popout link.
 * @param {string} url The target URL.
 * @param {Object} opt_window Window dependency injection for testing only.
 */
CodewalkViewer.prototype.navigateToCode = function(url, opt_window) {
  if (!opt_window) opt_window = window;
  // Each iframe is represented by two distinct objects in the DOM:  an iframe
  // object and a window object.  These do not expose the same capabilities.
  // Here we need to get the window representation to get the location member,
  // so we access it directly through window[] since jQuery returns the iframe
  // representation.
  // We replace location rather than set so as not to create a history for code
  // navigation.
  opt_window['code-display'].location.replace(url);
  var k = url.indexOf('&');
  if (k != -1) url = url.slice(0, k);
  k = url.indexOf('fileprint=');
  if (k != -1) url = url.slice(k+10, url.length);
  this.context.find('#code-popout-link').attr('href', url);
};

/**
 * Selects the first comment from the list and forces a refresh of the code
 * view.
 */
CodewalkViewer.prototype.selectFirstComment = function() {
  // TODO(rsc): handle case where there are no comments
  var firstSourcererLink = this.context.find('.comment:first');
  this.changeSelectedComment(firstSourcererLink);
};

/**
 * Sets the target on all links nested inside comments to be _blank.
 */
CodewalkViewer.prototype.targetCommentLinksAtBlank = function() {
  this.context.find('.comment a[href], #description a[href]').each(function() {
    if (!this.target) this.target = '_blank';
  });
};

/**
 * Installs event handlers for all the events we care about.
 */
CodewalkViewer.prototype.installEventHandlers = function() {
  var self = this;

  this.context.find('.comment')
      .click(function(event) {
        if (jQuery(event.target).is('a[href]')) return true;
        self.changeSelectedComment(jQuery(this));
        return false;
      });

  this.context.find('#code-selector')
      .change(function() {self.navigateToCode(jQuery(this).val());});

  this.context.find('#description-table .quote-feet.setting')
      .click(function() {self.toggleDescription(jQuery(this)); return false;});

  this.sizer
      .mousedown(function(ev) {self.startSizerDrag(ev); return false;});
  this.overlay
      .mouseup(function(ev) {self.endSizerDrag(ev); return false;})
      .mousemove(function(ev) {self.handleSizerDrag(ev); return false;});

  this.context.find('#prev-comment')
      .click(function() {
          self.changeSelectedComment(self.lastSelected.prev()); return false;
      });

  this.context.find('#next-comment')
      .click(function() {
          self.changeSelectedComment(self.lastSelected.next()); return false;
      });

  // Workaround for Firefox 2 and 3, which steal focus from the main document
  // whenever the iframe content is (re)loaded.  The input field is not shown,
  // but is a way for us to bring focus back to a place where we can detect
  // keypresses.
  this.context.find('#code-display')
      .load(function(ev) {self.shortcutInput.focus();});

  jQuery(document).keypress(function(ev) {
    switch(ev.which) {
      case 110:  // 'n'
          self.changeSelectedComment(self.lastSelected.next());
          return false;
      case 112:  // 'p'
          self.changeSelectedComment(self.lastSelected.prev());
          return false;
      default:  // ignore
    }
  });

  window.onresize = function() {self.updateHeight();};
};

/**
 * Starts dragging the pane sizer.
 * @param {Object} ev The mousedown event that started us dragging.
 */
CodewalkViewer.prototype.startSizerDrag = function(ev) {
  this.initialCodeWidth = this.codeColumn.width();
  this.initialCommentsWidth = this.commentColumn.width();
  this.initialMouseX = ev.pageX;
  this.overlay.show();
};

/**
 * Handles dragging the pane sizer.
 * @param {Object} ev The mousemove event updating dragging position.
 */
CodewalkViewer.prototype.handleSizerDrag = function(ev) {
  var delta = ev.pageX - this.initialMouseX;
  if (this.codeColumn.is('.right')) delta = -delta;
  var proposedCodeWidth = this.initialCodeWidth + delta;
  var proposedCommentWidth = this.initialCommentsWidth - delta;
  var mw = CodewalkViewer.MIN_PANE_WIDTH;
  if (proposedCodeWidth < mw) delta = mw - this.initialCodeWidth;
  if (proposedCommentWidth < mw) delta = this.initialCommentsWidth - mw;
  proposedCodeWidth = this.initialCodeWidth + delta;
  proposedCommentWidth = this.initialCommentsWidth - delta;
  // If window is too small, don't even try to resize.
  if (proposedCodeWidth < mw || proposedCommentWidth < mw) return;
  this.codeColumn.width(proposedCodeWidth);
  this.commentColumn.width(proposedCommentWidth);
  this.options.codeWidth = parseInt(
      this.codeColumn.width() /
      (this.codeColumn.width() + this.commentColumn.width()) * 100);
  this.context.find('#code-column-width').text(this.options.codeWidth + '%');
};

/**
 * Ends dragging the pane sizer.
 * @param {Object} ev The mouseup event that caused us to stop dragging.
 */
CodewalkViewer.prototype.endSizerDrag = function(ev) {
  this.overlay.hide();
  this.updateHeight();
};

/**
 * Toggles the Codewalk description between being shown and hidden.
 * @param {jQuery} target The target that was clicked to trigger this function.
 */
CodewalkViewer.prototype.toggleDescription = function(target) {
  var description = this.context.find('#description');
  description.toggle();
  target.find('span').text(description.is(':hidden') ? 'show' : 'hide');
  this.updateHeight();
};

/**
 * Changes the side of the window on which the code is shown and saves the
 * setting in a cookie.
 * @param {string?} codeSide The side on which the code should be, either
 *     'left' or 'right'.
 */
CodewalkViewer.prototype.changeCodeSide = function(codeSide) {
  var commentSide = codeSide == 'left' ? 'right' : 'left';
  this.context.find('#set-code-' + codeSide).addClass('selected');
  this.context.find('#set-code-' + commentSide).removeClass('selected');
  // Remove previous side class and add new one.
  this.codeColumn.addClass(codeSide).removeClass(commentSide);
  this.commentColumn.addClass(commentSide).removeClass(codeSide);
  this.sizer.css(codeSide, 'auto').css(commentSide, 0);
  this.options.codeSide = codeSide;
};

/**
 * Adds selected class to newly selected comment, removes selected style from
 * previously selected comment, changes drop down options so that the correct
 * file is selected, and updates the code popout link.
 * @param {jQuery} target The target that was clicked to trigger this function.
 */
CodewalkViewer.prototype.changeSelectedComment = function(target) {
  var currentFile = target.find('.comment-link').attr('href');
  if (!currentFile) return;

  if (!(this.lastSelected && this.lastSelected.get(0) === target.get(0))) {
    if (this.lastSelected) this.lastSelected.removeClass('selected');
    target.addClass('selected');
    this.lastSelected = target;
    var targetTop = target.position().top;
    var parentTop = target.parent().position().top;
    if (targetTop + target.height() > parentTop + target.parent().height() ||
        targetTop < parentTop) {
      var delta = targetTop - parentTop;
      target.parent().animate(
          {'scrollTop': target.parent().scrollTop() + delta},
          Math.max(delta / 2, 200), 'swing');
    }
    var fname = currentFile.match(/(?:select=|fileprint=)\/[^&]+/)[0];
    fname = fname.slice(fname.indexOf('=')+2, fname.length);
    this.context.find('#code-selector').val(fname);
    this.context.find('#prev-comment').toggleClass(
        'disabled', !target.prev().length);
    this.context.find('#next-comment').toggleClass(
        'disabled', !target.next().length);
  }

  // Force original file even if user hasn't changed comments since they may
  // have nagivated away from it within the iframe without us knowing.
  this.navigateToCode(currentFile);
};

/**
 * Updates the viewer by changing the height of the comments and code so that
 * they fit within the height of the window.  The function is typically called
 * after the user changes the window size.
 */
CodewalkViewer.prototype.updateHeight = function() {
  var windowHeight = jQuery(window).height() - 5  // GOK
  var areaHeight = windowHeight - this.codeArea.offset().top
  var footerHeight = this.context.find('#footer').outerHeight(true)
  this.commentArea.height(areaHeight - footerHeight - this.context.find('#comment-options').outerHeight(true))
  var codeHeight = areaHeight - footerHeight - 15  // GOK
  this.codeArea.height(codeHeight)
  this.codeDisplay.height(codeHeight - this.codeDisplay.offset().top + this.codeArea.offset().top);
  this.sizer.height(codeHeight);
};

jQuery(document).ready(function() {
  var viewer = new CodewalkViewer(jQuery());
  viewer.selectFirstComment();
  viewer.targetCommentLinksAtBlank();
  viewer.installEventHandlers();
  viewer.updateHeight();
});
<commit_msg>doc/codewalk: make it work with jQuery 1.8.2     so that it could work with the bundled jQuery.<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/**
 * A class to hold information about the Codewalk Viewer.
 * @param {jQuery} context The top element in whose context the viewer should
 *     operate.  It will not touch any elements above this one.
 * @constructor
 */
 var CodewalkViewer = function(context) {
  this.context = context;

  /**
   * The div that contains all of the comments and their controls.
   */
  this.commentColumn = this.context.find('#comment-column');

  /**
   * The div that contains the comments proper.
   */
  this.commentArea = this.context.find('#comment-area');

  /**
   * The div that wraps the iframe with the code, as well as the drop down menu
   * listing the different files.
   * @type {jQuery}
   */
  this.codeColumn = this.context.find('#code-column');

  /**
   * The div that contains the code but excludes the options strip.
   * @type {jQuery}
   */
  this.codeArea = this.context.find('#code-area');

  /**
   * The iframe that holds the code (from Sourcerer).
   * @type {jQuery}
   */
  this.codeDisplay = this.context.find('#code-display');

  /**
   * The overlaid div used as a grab handle for sizing the code/comment panes.
   * @type {jQuery}
   */
  this.sizer = this.context.find('#sizer');

  /**
   * The full-screen overlay that ensures we don't lose track of the mouse
   * while dragging.
   * @type {jQuery}
   */
  this.overlay = this.context.find('#overlay');

  /**
   * The hidden input field that we use to hold the focus so that we can detect
   * shortcut keypresses.
   * @type {jQuery}
   */
  this.shortcutInput = this.context.find('#shortcut-input');

  /**
   * The last comment that was selected.
   * @type {jQuery}
   */
  this.lastSelected = null;
};

/**
 * Minimum width of the comments or code pane, in pixels.
 * @type {number}
 */
CodewalkViewer.MIN_PANE_WIDTH = 200;

/**
 * Navigate the code iframe to the given url and update the code popout link.
 * @param {string} url The target URL.
 * @param {Object} opt_window Window dependency injection for testing only.
 */
CodewalkViewer.prototype.navigateToCode = function(url, opt_window) {
  if (!opt_window) opt_window = window;
  // Each iframe is represented by two distinct objects in the DOM:  an iframe
  // object and a window object.  These do not expose the same capabilities.
  // Here we need to get the window representation to get the location member,
  // so we access it directly through window[] since jQuery returns the iframe
  // representation.
  // We replace location rather than set so as not to create a history for code
  // navigation.
  opt_window['code-display'].location.replace(url);
  var k = url.indexOf('&');
  if (k != -1) url = url.slice(0, k);
  k = url.indexOf('fileprint=');
  if (k != -1) url = url.slice(k+10, url.length);
  this.context.find('#code-popout-link').attr('href', url);
};

/**
 * Selects the first comment from the list and forces a refresh of the code
 * view.
 */
CodewalkViewer.prototype.selectFirstComment = function() {
  // TODO(rsc): handle case where there are no comments
  var firstSourcererLink = this.context.find('.comment:first');
  this.changeSelectedComment(firstSourcererLink);
};

/**
 * Sets the target on all links nested inside comments to be _blank.
 */
CodewalkViewer.prototype.targetCommentLinksAtBlank = function() {
  this.context.find('.comment a[href], #description a[href]').each(function() {
    if (!this.target) this.target = '_blank';
  });
};

/**
 * Installs event handlers for all the events we care about.
 */
CodewalkViewer.prototype.installEventHandlers = function() {
  var self = this;

  this.context.find('.comment')
      .click(function(event) {
        if (jQuery(event.target).is('a[href]')) return true;
        self.changeSelectedComment(jQuery(this));
        return false;
      });

  this.context.find('#code-selector')
      .change(function() {self.navigateToCode(jQuery(this).val());});

  this.context.find('#description-table .quote-feet.setting')
      .click(function() {self.toggleDescription(jQuery(this)); return false;});

  this.sizer
      .mousedown(function(ev) {self.startSizerDrag(ev); return false;});
  this.overlay
      .mouseup(function(ev) {self.endSizerDrag(ev); return false;})
      .mousemove(function(ev) {self.handleSizerDrag(ev); return false;});

  this.context.find('#prev-comment')
      .click(function() {
          self.changeSelectedComment(self.lastSelected.prev()); return false;
      });

  this.context.find('#next-comment')
      .click(function() {
          self.changeSelectedComment(self.lastSelected.next()); return false;
      });

  // Workaround for Firefox 2 and 3, which steal focus from the main document
  // whenever the iframe content is (re)loaded.  The input field is not shown,
  // but is a way for us to bring focus back to a place where we can detect
  // keypresses.
  this.context.find('#code-display')
      .load(function(ev) {self.shortcutInput.focus();});

  jQuery(document).keypress(function(ev) {
    switch(ev.which) {
      case 110:  // 'n'
          self.changeSelectedComment(self.lastSelected.next());
          return false;
      case 112:  // 'p'
          self.changeSelectedComment(self.lastSelected.prev());
          return false;
      default:  // ignore
    }
  });

  window.onresize = function() {self.updateHeight();};
};

/**
 * Starts dragging the pane sizer.
 * @param {Object} ev The mousedown event that started us dragging.
 */
CodewalkViewer.prototype.startSizerDrag = function(ev) {
  this.initialCodeWidth = this.codeColumn.width();
  this.initialCommentsWidth = this.commentColumn.width();
  this.initialMouseX = ev.pageX;
  this.overlay.show();
};

/**
 * Handles dragging the pane sizer.
 * @param {Object} ev The mousemove event updating dragging position.
 */
CodewalkViewer.prototype.handleSizerDrag = function(ev) {
  var delta = ev.pageX - this.initialMouseX;
  if (this.codeColumn.is('.right')) delta = -delta;
  var proposedCodeWidth = this.initialCodeWidth + delta;
  var proposedCommentWidth = this.initialCommentsWidth - delta;
  var mw = CodewalkViewer.MIN_PANE_WIDTH;
  if (proposedCodeWidth < mw) delta = mw - this.initialCodeWidth;
  if (proposedCommentWidth < mw) delta = this.initialCommentsWidth - mw;
  proposedCodeWidth = this.initialCodeWidth + delta;
  proposedCommentWidth = this.initialCommentsWidth - delta;
  // If window is too small, don't even try to resize.
  if (proposedCodeWidth < mw || proposedCommentWidth < mw) return;
  this.codeColumn.width(proposedCodeWidth);
  this.commentColumn.width(proposedCommentWidth);
  this.options.codeWidth = parseInt(
      this.codeColumn.width() /
      (this.codeColumn.width() + this.commentColumn.width()) * 100);
  this.context.find('#code-column-width').text(this.options.codeWidth + '%');
};

/**
 * Ends dragging the pane sizer.
 * @param {Object} ev The mouseup event that caused us to stop dragging.
 */
CodewalkViewer.prototype.endSizerDrag = function(ev) {
  this.overlay.hide();
  this.updateHeight();
};

/**
 * Toggles the Codewalk description between being shown and hidden.
 * @param {jQuery} target The target that was clicked to trigger this function.
 */
CodewalkViewer.prototype.toggleDescription = function(target) {
  var description = this.context.find('#description');
  description.toggle();
  target.find('span').text(description.is(':hidden') ? 'show' : 'hide');
  this.updateHeight();
};

/**
 * Changes the side of the window on which the code is shown and saves the
 * setting in a cookie.
 * @param {string?} codeSide The side on which the code should be, either
 *     'left' or 'right'.
 */
CodewalkViewer.prototype.changeCodeSide = function(codeSide) {
  var commentSide = codeSide == 'left' ? 'right' : 'left';
  this.context.find('#set-code-' + codeSide).addClass('selected');
  this.context.find('#set-code-' + commentSide).removeClass('selected');
  // Remove previous side class and add new one.
  this.codeColumn.addClass(codeSide).removeClass(commentSide);
  this.commentColumn.addClass(commentSide).removeClass(codeSide);
  this.sizer.css(codeSide, 'auto').css(commentSide, 0);
  this.options.codeSide = codeSide;
};

/**
 * Adds selected class to newly selected comment, removes selected style from
 * previously selected comment, changes drop down options so that the correct
 * file is selected, and updates the code popout link.
 * @param {jQuery} target The target that was clicked to trigger this function.
 */
CodewalkViewer.prototype.changeSelectedComment = function(target) {
  var currentFile = target.find('.comment-link').attr('href');
  if (!currentFile) return;

  if (!(this.lastSelected && this.lastSelected.get(0) === target.get(0))) {
    if (this.lastSelected) this.lastSelected.removeClass('selected');
    target.addClass('selected');
    this.lastSelected = target;
    var targetTop = target.position().top;
    var parentTop = target.parent().position().top;
    if (targetTop + target.height() > parentTop + target.parent().height() ||
        targetTop < parentTop) {
      var delta = targetTop - parentTop;
      target.parent().animate(
          {'scrollTop': target.parent().scrollTop() + delta},
          Math.max(delta / 2, 200), 'swing');
    }
    var fname = currentFile.match(/(?:select=|fileprint=)\/[^&]+/)[0];
    fname = fname.slice(fname.indexOf('=')+2, fname.length);
    this.context.find('#code-selector').val(fname);
    this.context.find('#prev-comment').toggleClass(
        'disabled', !target.prev().length);
    this.context.find('#next-comment').toggleClass(
        'disabled', !target.next().length);
  }

  // Force original file even if user hasn't changed comments since they may
  // have nagivated away from it within the iframe without us knowing.
  this.navigateToCode(currentFile);
};

/**
 * Updates the viewer by changing the height of the comments and code so that
 * they fit within the height of the window.  The function is typically called
 * after the user changes the window size.
 */
CodewalkViewer.prototype.updateHeight = function() {
  var windowHeight = jQuery(window).height() - 5  // GOK
  var areaHeight = windowHeight - this.codeArea.offset().top
  var footerHeight = this.context.find('#footer').outerHeight(true)
  this.commentArea.height(areaHeight - footerHeight - this.context.find('#comment-options').outerHeight(true))
  var codeHeight = areaHeight - footerHeight - 15  // GOK
  this.codeArea.height(codeHeight)
  this.codeDisplay.height(codeHeight - this.codeDisplay.offset().top + this.codeArea.offset().top);
  this.sizer.height(codeHeight);
};

jQuery(document).ready(function() {
  var viewer = new CodewalkViewer(jQuery('#codewalk-main'));
  viewer.selectFirstComment();
  viewer.targetCommentLinksAtBlank();
  viewer.installEventHandlers();
  viewer.updateHeight();
});
<|endoftext|>