<commit_before>pc.extend(pc, function () {
    /**
     * @component
     * @name pc.LayoutGroupComponent
     * @description Create a new LayoutGroupComponent
     * @class A LayoutGroupComponent enables the Entity to position and scale child {@link pc.ElementComponent}s according to configurable layout rules.
     * @param {pc.LayoutGroupComponentSystem} system The ComponentSystem that created this Component
     * @param {pc.Entity} entity The Entity that this Component is attached to.
     * @extends pc.Component
     * @property {pc.ORIENTATION} orientation Whether the layout should run horizontally or vertically.
     * @property {Boolean} reverseX Reverses the order of children along the x axis.
     * @property {Boolean} reverseY Reverses the order of children along the y axis.
     * @property {pc.Vec2} alignment Specifies the horizontal and vertical alignment of child elements. Values range from 0 to 1 where [0,0] is the bottom left and [1,1] is the top right.
     * @property {pc.Vec4} padding Padding to be applied inside the container before positioning any children. Specified as left, bottom, right and top values.
     * @property {pc.Vec2} spacing Spacing to be applied between each child element.
     * @property {pc.FITTING} widthFitting Fitting logic to be applied when positioning and scaling child elements. Can be one of the following:
     *

     *
{@link pc.FITTING_NONE}: Child elements will be rendered at their natural size.

     *

     *         {@link pc.FITTING_STRETCH}: When the natural size of all child elements is not sufficient to fill the width of the container, children will be stretched to fit. The rules for how each child will be stretched are outlined below:
     *

     *
Sum the {@link pc.LayoutChildComponent#fitWidthProportion} values of each child and normalize so that all values sum to 1.

     *
Apply the {@link pc.LayoutChildComponent#minWidth} for each child.

     *
If there is space remaining in the container, apply the natural {@link pc.LayoutChildComponent#width} of each child.

     *
If the new total width exceeds the available space of the container, reduce each child's width proportionally based on the normalized {@link pc.LayoutChildComponent#fitWidthProportion} values.

     *
If there is space remaining in the container, distribute it to each child based on the normalized {@link pc.LayoutChildComponent#fitWidthProportion} values, but do not exceed the {@link pc.LayoutChildComponent#maxWidth} of each child.

     *

     *

     *

     *         {@link pc.FITTING_SHRINK}: When the natural size of all child elements overflows the width of the container, children will be shrunk to fit. The rules for how each child will be stretched are outlined below.
     *

     *
Sum the {@link pc.LayoutChildComponent#fitWidthProportion} values of each child and normalize so that all values sum to 1.

     *
Reduce each child's {@link pc.LayoutChildComponent#width} proportionally based on the normalized {@link pc.LayoutChildComponent#fitWidthProportion} values, but do not exceed the {@link pc.LayoutChildComponent#minWidth} of each child.

     *

     *

     *
{@link pc.FITTING_BOTH}: Applies both STRETCH and SHRINK logic as necessary.

     *

     *

     * @property {pc.FITTING} heightFitting Identical to {@link pc.LayoutGroupComponent#widthFitting} but for the Y axis.
     * @property {Boolean} wrap Whether or not to wrap children onto a new row/column when the size of the container is exceeded. Defaults to false, which means that children will be be rendered in a single row (horizontal orientation) or column (vertical orientation).

Note that setting wrap to true makes it impossible for the {@link pc.FITTING_BOTH} fitting mode to operate in any logical manner. For this reason, when wrap is true, a {@link pc.LayoutGroupComponent#widthFitting} or {@link pc.LayoutGroupComponent#heightFitting} mode of {@link pc.FITTING_BOTH} will be coerced to {@link pc.FITTING_STRETCH}.
     */
    var LayoutGroupComponent = function LayoutGroupComponent(system, entity) {
        this._orientation = pc.ORIENTATION_HORIZONTAL;
        this._reverseX = false;
        this._reverseY = false;
        this._alignment = new pc.Vec2();
        this._padding = new pc.Vec4();
        this._spacing = new pc.Vec2();
        this._widthFitting = pc.FITTING_NONE;
        this._heightFitting = pc.FITTING_NONE;
        this._wrap = false;
        this._layoutCalculator = new pc.LayoutCalculator();

        // Listen for the group container being resized
        this._listenForResizeEvents(this.entity, 'on');

        // Listen to existing children being resized
        this.entity.children.forEach(function(child) {
            this._listenForResizeEvents(child, 'on');
        }.bind(this));

        // Listen to newly added children being resized
        this.entity.on('childinsert', this._onChildInsert, this);
        this.entity.on('childremove', this._onChildRemove, this);

        // Listen for ElementComponents and LayoutChildComponents being added
        // to self or to children - covers cases where they are not already
        // present at the point when this component is constructed.
        pc.app.systems.element.on('add', this._onElementComponentAdd, this);
        pc.app.systems.element.on('beforeremove', this._onElementComponentRemove, this);
        pc.app.systems.layoutchild.on('add', this._onLayoutChildComponentAdd, this);
        pc.app.systems.layoutchild.on('beforeremove', this._onLayoutChildComponentRemove, this);

        if (this._selfAndChildrenAreAllElements()) {
            this._scheduleReflow();
        }
    };
    LayoutGroupComponent = pc.inherits(LayoutGroupComponent, pc.Component);

    pc.extend(LayoutGroupComponent.prototype, {
        _selfAndChildrenAreAllElements: function() {
            return this.entity.element && this.entity.children.every(getElement);
        },

        _listenForResizeEvents: function(target, onOff) {
            if (target.element) {
                target.element[onOff]('resize', this._onResize, this);
            }

            if (target.layoutchild) {
                target.layoutchild[onOff]('resize', this._onResize, this);
            }
        },

        _onElementComponentAdd: function(entity, component) {
            component.on('resize', this._onResize, this);
        },

        _onElementComponentRemove: function(entity, component) {
            component.off('resize', this._onResize, this);
        },

        _onLayoutChildComponentAdd: function(entity, component) {
            component.on('resize', this._onResize, this);
        },

        _onLayoutChildComponentRemove: function(entity, component) {
            component.off('resize', this._onResize, this);
        },

        _onResize: function() {
            this._scheduleReflow();
        },

        _onChildInsert: function(child) {
            this._listenForResizeEvents(child, 'on');
            this._scheduleReflow();
        },

        _onChildRemove: function(child) {
            this._listenForResizeEvents(child, 'off');
            this._scheduleReflow();
        },

        _scheduleReflow: function() {
            if (!this._isPerformingReflow) {
                this.fire('schedulereflow', this);
            }
        },

        reflow: function() {
            if (!this._selfAndChildrenAreAllElements()) {
                return;
            }

            var elements = this.entity.children.map(getElement);
            var container = getElement(this.entity);

            var options = {
                orientation: this._orientation,
                reverseX: this._reverseX,
                reverseY: this._reverseY,
                alignment: this._alignment,
                padding: this._padding,
                spacing: this._spacing,
                widthFitting: this._widthFitting,
                heightFitting: this._heightFitting,
                wrap: this._wrap,
                containerSize: new pc.Vec2(container.calculatedWidth, container.calculatedHeight)
            };

            // In order to prevent recursive reflow (i.e. whereby setting the size of
            // a child element triggers another reflow on the next frame, and so on)
            // we flag that a reflow is currently in progress.
            this._isPerformingReflow = true;
            this._layoutCalculator.calculateLayout(elements, options);
            this._isPerformingReflow = false;
        },

        onRemove: function () {
            this.entity.off('insert', this._onResize, this);

            this.entity.children.forEach(function(child) {
                this._listenForResizeEvents(child, 'off');
            }.bind(this));
        }
    });

    function getElement(entity) {
        return entity.element;
    }

    function defineReflowSchedulingProperty(name) {
        var _name = '_' + name;

        Object.defineProperty(LayoutGroupComponent.prototype, name, {
            get: function () {
                return this[_name];
            },

            set: function (value) {
                if (this[_name] !== value) {
                    this[_name] = value;
                    this._scheduleReflow();
                }
            }
        });
    }

    defineReflowSchedulingProperty('orientation');
    defineReflowSchedulingProperty('reverseX');
    defineReflowSchedulingProperty('reverseY');
    defineReflowSchedulingProperty('alignment');
    defineReflowSchedulingProperty('padding');
    defineReflowSchedulingProperty('spacing');
    defineReflowSchedulingProperty('widthFitting');
    defineReflowSchedulingProperty('heightFitting');
    defineReflowSchedulingProperty('wrap');

    return {
        LayoutGroupComponent: LayoutGroupComponent
    };
}());
<commit_msg>Fix undefined reference error when launching game<commit_after>pc.extend(pc, function () {
    /**
     * @component
     * @name pc.LayoutGroupComponent
     * @description Create a new LayoutGroupComponent
     * @class A LayoutGroupComponent enables the Entity to position and scale child {@link pc.ElementComponent}s according to configurable layout rules.
     * @param {pc.LayoutGroupComponentSystem} system The ComponentSystem that created this Component
     * @param {pc.Entity} entity The Entity that this Component is attached to.
     * @extends pc.Component
     * @property {pc.ORIENTATION} orientation Whether the layout should run horizontally or vertically.
     * @property {Boolean} reverseX Reverses the order of children along the x axis.
     * @property {Boolean} reverseY Reverses the order of children along the y axis.
     * @property {pc.Vec2} alignment Specifies the horizontal and vertical alignment of child elements. Values range from 0 to 1 where [0,0] is the bottom left and [1,1] is the top right.
     * @property {pc.Vec4} padding Padding to be applied inside the container before positioning any children. Specified as left, bottom, right and top values.
     * @property {pc.Vec2} spacing Spacing to be applied between each child element.
     * @property {pc.FITTING} widthFitting Fitting logic to be applied when positioning and scaling child elements. Can be one of the following:
     *

     *
{@link pc.FITTING_NONE}: Child elements will be rendered at their natural size.

     *

     *         {@link pc.FITTING_STRETCH}: When the natural size of all child elements is not sufficient to fill the width of the container, children will be stretched to fit. The rules for how each child will be stretched are outlined below:
     *

     *
Sum the {@link pc.LayoutChildComponent#fitWidthProportion} values of each child and normalize so that all values sum to 1.

     *
Apply the {@link pc.LayoutChildComponent#minWidth} for each child.

     *
If there is space remaining in the container, apply the natural {@link pc.LayoutChildComponent#width} of each child.

     *
If the new total width exceeds the available space of the container, reduce each child's width proportionally based on the normalized {@link pc.LayoutChildComponent#fitWidthProportion} values.

     *
If there is space remaining in the container, distribute it to each child based on the normalized {@link pc.LayoutChildComponent#fitWidthProportion} values, but do not exceed the {@link pc.LayoutChildComponent#maxWidth} of each child.

     *

     *

     *

     *         {@link pc.FITTING_SHRINK}: When the natural size of all child elements overflows the width of the container, children will be shrunk to fit. The rules for how each child will be stretched are outlined below.
     *

     *
Sum the {@link pc.LayoutChildComponent#fitWidthProportion} values of each child and normalize so that all values sum to 1.

     *
Reduce each child's {@link pc.LayoutChildComponent#width} proportionally based on the normalized {@link pc.LayoutChildComponent#fitWidthProportion} values, but do not exceed the {@link pc.LayoutChildComponent#minWidth} of each child.

     *

     *

     *
{@link pc.FITTING_BOTH}: Applies both STRETCH and SHRINK logic as necessary.

     *

     *

     * @property {pc.FITTING} heightFitting Identical to {@link pc.LayoutGroupComponent#widthFitting} but for the Y axis.
     * @property {Boolean} wrap Whether or not to wrap children onto a new row/column when the size of the container is exceeded. Defaults to false, which means that children will be be rendered in a single row (horizontal orientation) or column (vertical orientation).

Note that setting wrap to true makes it impossible for the {@link pc.FITTING_BOTH} fitting mode to operate in any logical manner. For this reason, when wrap is true, a {@link pc.LayoutGroupComponent#widthFitting} or {@link pc.LayoutGroupComponent#heightFitting} mode of {@link pc.FITTING_BOTH} will be coerced to {@link pc.FITTING_STRETCH}.
     */
    var LayoutGroupComponent = function LayoutGroupComponent(system, entity) {
        this._orientation = pc.ORIENTATION_HORIZONTAL;
        this._reverseX = false;
        this._reverseY = false;
        this._alignment = new pc.Vec2();
        this._padding = new pc.Vec4();
        this._spacing = new pc.Vec2();
        this._widthFitting = pc.FITTING_NONE;
        this._heightFitting = pc.FITTING_NONE;
        this._wrap = false;
        this._layoutCalculator = new pc.LayoutCalculator();

        // Listen for the group container being resized
        this._listenForResizeEvents(this.entity, 'on');

        // Listen to existing children being resized
        this.entity.children.forEach(function(child) {
            this._listenForResizeEvents(child, 'on');
        }.bind(this));

        // Listen to newly added children being resized
        this.entity.on('childinsert', this._onChildInsert, this);
        this.entity.on('childremove', this._onChildRemove, this);

        // Listen for ElementComponents and LayoutChildComponents being added
        // to self or to children - covers cases where they are not already
        // present at the point when this component is constructed.
        system.app.systems.element.on('add', this._onElementComponentAdd, this);
        system.app.systems.element.on('beforeremove', this._onElementComponentRemove, this);
        system.app.systems.layoutchild.on('add', this._onLayoutChildComponentAdd, this);
        system.app.systems.layoutchild.on('beforeremove', this._onLayoutChildComponentRemove, this);

        if (this._selfAndChildrenAreAllElements()) {
            this._scheduleReflow();
        }
    };
    LayoutGroupComponent = pc.inherits(LayoutGroupComponent, pc.Component);

    pc.extend(LayoutGroupComponent.prototype, {
        _selfAndChildrenAreAllElements: function() {
            return this.entity.element && this.entity.children.every(getElement);
        },

        _listenForResizeEvents: function(target, onOff) {
            if (target.element) {
                target.element[onOff]('resize', this._onResize, this);
            }

            if (target.layoutchild) {
                target.layoutchild[onOff]('resize', this._onResize, this);
            }
        },

        _onElementComponentAdd: function(entity, component) {
            component.on('resize', this._onResize, this);
        },

        _onElementComponentRemove: function(entity, component) {
            component.off('resize', this._onResize, this);
        },

        _onLayoutChildComponentAdd: function(entity, component) {
            component.on('resize', this._onResize, this);
        },

        _onLayoutChildComponentRemove: function(entity, component) {
            component.off('resize', this._onResize, this);
        },

        _onResize: function() {
            this._scheduleReflow();
        },

        _onChildInsert: function(child) {
            this._listenForResizeEvents(child, 'on');
            this._scheduleReflow();
        },

        _onChildRemove: function(child) {
            this._listenForResizeEvents(child, 'off');
            this._scheduleReflow();
        },

        _scheduleReflow: function() {
            if (!this._isPerformingReflow) {
                this.fire('schedulereflow', this);
            }
        },

        reflow: function() {
            if (!this._selfAndChildrenAreAllElements()) {
                return;
            }

            var elements = this.entity.children.map(getElement);
            var container = getElement(this.entity);

            var options = {
                orientation: this._orientation,
                reverseX: this._reverseX,
                reverseY: this._reverseY,
                alignment: this._alignment,
                padding: this._padding,
                spacing: this._spacing,
                widthFitting: this._widthFitting,
                heightFitting: this._heightFitting,
                wrap: this._wrap,
                containerSize: new pc.Vec2(container.calculatedWidth, container.calculatedHeight)
            };

            // In order to prevent recursive reflow (i.e. whereby setting the size of
            // a child element triggers another reflow on the next frame, and so on)
            // we flag that a reflow is currently in progress.
            this._isPerformingReflow = true;
            this._layoutCalculator.calculateLayout(elements, options);
            this._isPerformingReflow = false;
        },

        onRemove: function () {
            this.entity.off('insert', this._onResize, this);

            this.entity.children.forEach(function(child) {
                this._listenForResizeEvents(child, 'off');
            }.bind(this));
        }
    });

    function getElement(entity) {
        return entity.element;
    }

    function defineReflowSchedulingProperty(name) {
        var _name = '_' + name;

        Object.defineProperty(LayoutGroupComponent.prototype, name, {
            get: function () {
                return this[_name];
            },

            set: function (value) {
                if (this[_name] !== value) {
                    this[_name] = value;
                    this._scheduleReflow();
                }
            }
        });
    }

    defineReflowSchedulingProperty('orientation');
    defineReflowSchedulingProperty('reverseX');
    defineReflowSchedulingProperty('reverseY');
    defineReflowSchedulingProperty('alignment');
    defineReflowSchedulingProperty('padding');
    defineReflowSchedulingProperty('spacing');
    defineReflowSchedulingProperty('widthFitting');
    defineReflowSchedulingProperty('heightFitting');
    defineReflowSchedulingProperty('wrap');

    return {
        LayoutGroupComponent: LayoutGroupComponent
    };
}());
<|endoftext|>


<commit_before>$(document).ready(function() {

	$('.scroll-link').on('click', function(event){
		event.preventDefault();
		var sectionID = $(this).attr("data-id");
		scrollToID('#' + sectionID, 750);
	});

	if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) {
		$(".timeline-text-container").on("click", function () {
			$(this).children(".timeline-hover-notice").hide();
			$(this).children(".timeline-class-desc").slideToggle();
		});
	} else {
		$(".timeline-text-container").on("mouseenter", function () {
			$(this).children(".timeline-hover-notice").hide();
			$(this).children(".timeline-class-desc").slideToggle();
		});

		$(".timeline-text-container").on("mouseleave", function () {
			$(this).children(".timeline-class-desc").slideToggle();
		});
	}

	navSetter('main');
	navSetter('timeline');
	navSetter('portfolio');

});
// scroll function
function scrollToID(id, speed){
	var targetOffset = $(id).offset().top -40;
	$('html,body').animate({scrollTop:targetOffset}, speed);
}

function navSetter(id) {

	var target = $("#"+id).offset().top -50,
	    timeout = null;

	$(window).scroll(function () {
	    if (!timeout) {
	        timeout = setTimeout(function () {
	            clearTimeout(timeout);
	            timeout = null;
	            if ($(window).scrollTop() >= target) {
	            	$(".nav").children().removeClass('active');
	                $("#nav-"+id).addClass('active');
	            }
	        }, 250);
	    }
	});

}
<commit_msg>Update script.js<commit_after>$(document).ready(function() {

	$('.scroll-link').on('click', function(event){
		event.preventDefault();
		var sectionID = $(this).attr("data-id");
		scrollToID('#' + sectionID, 750);
	});

	if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) {
		$(".timeline-text-container").on("click", function () {
			$(this).children(".timeline-hover-notice").hide();
			$(this).children(".timeline-class-desc").slideToggle();
		});
	} else {
		$('.project').hover(function(){
			$(this).find(".hover").slideToggle();
		//$(this).find(".hover").fadeIn(300);
		});
		/*$(".timeline-text-container").on("mouseenter", function () {
			$(this).children(".timeline-hover-notice").hide();
			$(this).children(".timeline-class-desc").slideToggle();
		});

		$(".timeline-text-container").on("mouseleave", function () {
			$(this).children(".timeline-class-desc").slideToggle();
		});*/
	}

	navSetter('main');
	navSetter('timeline');
	navSetter('portfolio');

});
// scroll function
function scrollToID(id, speed){
	var targetOffset = $(id).offset().top -40;
	$('html,body').animate({scrollTop:targetOffset}, speed);
}

function navSetter(id) {

	var target = $("#"+id).offset().top -50,
	    timeout = null;

	$(window).scroll(function () {
	    if (!timeout) {
	        timeout = setTimeout(function () {
	            clearTimeout(timeout);
	            timeout = null;
	            if ($(window).scrollTop() >= target) {
	            	$(".nav").children().removeClass('active');
	                $("#nav-"+id).addClass('active');
	            }
	        }, 250);
	    }
	});

}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
'use strict';

var fs = require('fs'),
    path = require('path'),
    rimraf = require('rimraf'),
    hashdir = require('./lib/hashdir');

function namer(modulePath, hash) {
    return modulePath + '@' + hash.slice(0, 6); // new directory name
}

function namefilter(item) {
    return (/^[\w\-]+$/).test(item); // match directory names for hashbuild()
}

function prefix(left) {
    return function(right) { return left + right; }; // use with Array.map
}

function forceRename(from, to, cb) {
    fs.exists(to, function(exists) {
        if (exists) {
            // delete destination dir before renaming
            rimraf(to, function() { fs.rename(from, to, cb); });
        } else {
            fs.rename(from, to, cb);
        }
    });
}

/**
 * Call hashdir.js for each directory path in an array.
 * @param {array} dirs Array of pathnames to call hashdir on
 * @param {object} options Options for hashdir.js
 * @param {function} callback Callback
 * @see ./hashdir.js
 */
function hashdirs(dirs, options, callback) {
    var count = dirs.length,
        results = {};

    if (!callback) {
        callback = options;
        options = {};
    }

    if (!options.namer) {
        options.namer = namer;
    }

    if (!options.hasOwnProperty('exec')) {
        options.exec = forceRename;
    }

    function isDone(err) {
        if (!--count) {
            callback(err, err ? null : results);
        }
    }

    function afterHash(err, result) {
        var moddir = result.dir,
            modname = path.basename(moddir),
            newdir = options.namer(result.dir, result.hash);

        if (!err) {
            // save result
            results[modname] = newdir;
        }

        if (!err && options.exec) {
            // rename
            options.exec(moddir, newdir, isDone);

        } else {
            // done yet?
            isDone(err);
        }
    }

    dirs.forEach(function (dir) {
        hashdir(dir, options, afterHash);
    });
}

/**
 * Call hashdir.js for each item in a shifter build directory (filtering
 * out item names not matched by namefilter()).
 * @param {string} buildDir Pathname of Shifter build directory
 * @param {object} options Options for hashdir.js
 * @param {function} callback Callback
 * @see ./hashdir.js
 */
function hashbuild(blddir, options, callback) {
    if (!callback) {
        callback = options;
        options = {};
    }

    fs.readdir(blddir, function (err, items) {
        var dirs;
        if (err) {
            callback(err);
        } else {
            dirs = items.filter(namefilter).map(prefix(blddir + path.sep));
            hashdirs(dirs, options, callback);
        }
    });
}

module.exports = {
    hashdirs: hashdirs,
    hashbuild: hashbuild,
    hashfile: hashdir.hashfile,
    forceRename: forceRename
};
<commit_msg>minor comment<commit_after>/*
 * Copyright (c) 2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
'use strict';

var fs = require('fs'),
    path = require('path'),
    rimraf = require('rimraf'),
    hashdir = require('./lib/hashdir');

function namer(modulePath, hash) {
    return modulePath + '@' + hash.slice(0, 6); // new directory name
}

function namefilter(item) {
    return (/^[\w\-]+$/).test(item); // match directory names for hashbuild()
}

function prefix(left) {
    return function(right) { return left + right; }; // use with Array.map
}

function forceRename(from, to, cb) {
    fs.exists(to, function(exists) {
        if (exists) {
            // delete destination dir before renaming
            rimraf(to, function() { fs.rename(from, to, cb); });
        } else {
            fs.rename(from, to, cb);
        }
    });
}

/**
 * Call hashdir.js for each directory path in an array.
 * @param {array} dirs Array of pathnames to call hashdir on
 * @param {object} options Options for hashdir.js
 * @param {function} callback Callback
 * @see ./hashdir.js
 */
function hashdirs(dirs, options, callback) {
    var count = dirs.length,
        results = {};

    if (!callback) {
        callback = options;
        options = {};
    }

    if (!options.namer) {
        options.namer = namer;
    }

    if (!options.hasOwnProperty('exec')) {  // falsey value skips exec
        options.exec = forceRename;         // default is to forceRename
    }

    function isDone(err) {
        if (!--count) {
            callback(err, err ? null : results);
        }
    }

    function afterHash(err, result) {
        var moddir = result.dir,
            modname = path.basename(moddir),
            newdir = options.namer(result.dir, result.hash);

        if (!err) {
            // save result
            results[modname] = newdir;
        }

        if (!err && options.exec) {
            // rename
            options.exec(moddir, newdir, isDone);

        } else {
            // done yet?
            isDone(err);
        }
    }

    dirs.forEach(function (dir) {
        hashdir(dir, options, afterHash);
    });
}

/**
 * Call hashdir.js for each item in a shifter build directory (filtering
 * out item names not matched by namefilter()).
 * @param {string} buildDir Pathname of Shifter build directory
 * @param {object} options Options for hashdir.js
 * @param {function} callback Callback
 * @see ./hashdir.js
 */
function hashbuild(blddir, options, callback) {
    if (!callback) {
        callback = options;
        options = {};
    }

    fs.readdir(blddir, function (err, items) {
        var dirs;
        if (err) {
            callback(err);
        } else {
            dirs = items.filter(namefilter).map(prefix(blddir + path.sep));
            hashdirs(dirs, options, callback);
        }
    });
}

module.exports = {
    hashdirs: hashdirs,
    hashbuild: hashbuild,
    hashfile: hashdir.hashfile,
    forceRename: forceRename
};
<|endoftext|>


<commit_before>/*
 * This file is part of the Sulu CMS.
 *
 * (c) MASSIVE ART WebServices GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

define([
    'config',
    'text!suluproduct/components/products/components/attributes/overlay-content.html'
], function(Config, OverlayTpl) {
    'use strict';

    var formSelector = '#product-attributes-form',
        productAttributesInstanceName = 'product-attribute-list-toolbar',
        datagridInstanceName = 'product-attribute-datagrid',
        overlayInstanceName = 'product-attribute-overlay',
        selectInstanceName = 'product-attribute-select',
        typeText = 'product.attribute.type.text',
        attributeId = null,
        maxLengthTitle = 60,
        actions = {
            ADD: 1,
            DELETE: 2,
            UPDATE: 3
        },

        /**
         * bind custom events
         */
        bindCustomEvents = function() {
            this.sandbox.on('sulu.header.toolbar.delete', function () {
                this.sandbox.emit('sulu.product.delete', this.options.data.id);
            }.bind(this));

            this.sandbox.on('product.state.change', function(id) {
                if (!this.options.data.status || this.options.data.status.id !== id) {
                    this.status = {id: id};
                    this.options.data.status = this.status;
                    setHeaderBar.call(this, false);
                }
            }, this);

            this.sandbox.on('sulu.header.back', function() {
                this.sandbox.emit('sulu.products.list');
            }, this);

            this.sandbox.on('sulu.header.toolbar.save', function() {
                this.sendData = {};
                this.sendData.status = this.status;
                save.call(this);
            }, this);

            this.sandbox.on('sulu.products.saved', function(data) {

                var attributes = data.attributes;

                // Select action
                if (data.action === actions.ADD) {
                    // ADD RECORD IN DATAGRID
                    var result = _.findWhere(attributes, {'attributeId': data.attributeId});
                    this.sandbox.emit('husky.datagrid.' + datagridInstanceName + '.record.add', {
                        id: result.id,
                        attributeId: result.attributeId,
                        value: result.value,
                        attributeName: result.attributeName
                    });
                } else if (data.action === actions.DELETE) {
                    // DELETE RECORDs IN DATAGRID
                    $.each(data.deleteIds, function(key, id) {
                        this.sandbox.emit('husky.datagrid.' + datagridInstanceName + '.record.remove', id);
                    }.bind(this));
                } else if (data.action === actions.UPDATE) {
                    // UPDATE RECORD IN DATAGRID
                    this.sandbox.emit('husky.datagrid.' + datagridInstanceName + '.records.change', attributes);
                }

                setHeaderBar.call(this, true);
                //this.options.data = data;
                this.options.data.attributes.status = this.status;
            }, this);
        },

        /**
         * modify header information
         */
        setHeaderBar = function(saved) {
            var type = (!!this.options.data && !!this.options.data.id) ? 'edit' : 'add';
            this.sandbox.emit('sulu.header.toolbar.state.change', type, saved, true);
        },

        /**
         * Create overlay content for add attribute overlay
         */
        createAddOverlayContent = function() {
            attributeId = null;

            // create container for overlay
            var $overlayContent = this.sandbox.dom.createElement(this.sandbox.util.template(OverlayTpl, {
                translate: this.sandbox.translate
            }));
            this.sandbox.dom.append(this.$el, $overlayContent);

            return $overlayContent;
        },

        /**
         * sets the header information
         */
        setHeaderInformation = function() {
            var title = 'pim.product.title',
                breadcrumb = [
                    {title: 'navigation.pim'},
                    {title: 'pim.products.title'}
                ];
            if (!!this.options.data && !!this.options.data.attributes.name) {
                title = this.options.data.attributes.name;
            }
            title = this.sandbox.util.cropTail(title, maxLengthTitle);
            this.sandbox.emit('sulu.header.set-title', title);

            if (!!this.options.data && !!this.options.data.attributes.number) {
                breadcrumb.push({
                    title: '#' + this.options.data.attributes.number
                });
            } else {
                breadcrumb.push({
                    title: 'pim.product.title'
                });
            }
            this.sandbox.emit('sulu.header.set-breadcrumb', breadcrumb);
        },

        /**
         * Create the overlay
         */
        createAddOverlay = function() {
            // call JSON to get the attributes from the server then create the overlay after it's done
            var ajaxRequest = $.getJSON('api/attributes', function(data) {
                this.attributeTypes = new Array();

                $.each(data._embedded.attributes, function(key, value) {
                    var newAttribute = {
                        'id': value.id,
                        'name': value.name
                    };

                    // at this time we support only text type attributes
                    if (value.type.name === typeText) {
                        this.attributeTypes.push(newAttribute);
                    }
                }.bind(this));
            }.bind(this));

            ajaxRequest.done(function() {
                // create container for overlay
                var $overlay = this.sandbox.dom.createElement('<div>');
                this.sandbox.dom.append(this.$el, $overlay);

                // create content
                this.sandbox.start([
                    {
                        name: 'overlay@husky',
                        options: {
                            el: $overlay,
                            supportKeyInput: false,
                            title: this.sandbox.translate('product.attribute.overlay.title'),
                            skin: 'normal',
                            openOnStart: true,
                            removeOnClose: true,
                            instanceName: overlayInstanceName,
                            data: createAddOverlayContent.call(this),
                            okCallback: overlayOkClicked.bind(this)
                        }
                    }
                ]);

            }.bind(this));

            ajaxRequest.fail(function() {
                console.log('Error retrieving attributes from server');
            }.bind(this));

            ajaxRequest.complete(function() {
                // create dropbox in overlay
                var selectOptions = {
                    el: '#selectBox',
                    instanceName: selectInstanceName,
                    multipleSelect: false,
                    defaultLabel: this.sandbox.translate('product.attribute.overlay.defaultlabel'),
                    valueName: 'name',
                    isNative: true,
                    data: this.attributeTypes
                };

                this.sandbox.start([
                    // start select
                    {
                        name: 'select@husky',
                        options: selectOptions
                    }
                ]);

                // define select event for dropbox
                this.sandbox.on('husky.select.' + selectInstanceName + '.selected.item', function(item) {
                    attributeId = parseInt(item);
                });
            }.bind(this));
        },

        /**
         * save product attributes
         */
        save = function() {
            this.sandbox.emit('sulu.products.save', this.sendData);
        },

        /**
         * called when OK on overlay was clicked
         */
        overlayOkClicked = function() {
            // exit if no attribute is selected in overlay
            if (!attributeId) {
                return;
            }

            this.sendData = new Object();
            var attributeValue = this.sandbox.dom.val('#attribute-name');

            var attributes = this.options.data.attributes.attributes;

            var result = _.findWhere(attributes, {'attributeId': attributeId});

            if (result) {
                result.value = attributeValue;
                this.sendData.action = actions.UPDATE;
            } else {
                var newAttribute = {
                    'attributeId': attributeId,
                    'value': attributeValue
                };
                attributes.push(newAttribute);
                this.sendData.action = actions.ADD;
            }

            this.sendData.attributeId = attributeId;
            this.sendData.attributes = attributes;
            this.sendData.status = this.status;
            this.sendData.id = this.options.data.attributes.id;

            save.call(this);
        },

        /**
         * delete action function from toolbar
         */
        attributeDelete = function() {
            this.sandbox.emit('husky.datagrid.' + datagridInstanceName + '.items.get-selected', function(ids) {

                var attributes = this.options.data.attributes.attributes;
                this.sendData = new Object();
                var deleteIds = new Array();

                _.each(ids, function(value, key, list) {
                    var result = _.findWhere(attributes, {'id': value});
                    attributes = _.without(attributes, result);
                    deleteIds.push(value);
                });

                this.sendData.deleteIds = deleteIds;
                this.sendData.attributes = attributes;
                this.sendData.status = this.status;
                this.sendData.id = this.options.data.id;
                this.sendData.action = actions.DELETE;

                save.call(this);
            }.bind(this));
        },

        /**
         * calls basic form components
         */
        startFormComponents = function() {
            var datagridOptions = {
                el: '#product-attribute-list',
                instanceName: datagridInstanceName,
                resultKey: 'attributes',
                matchings: [
                    {
                        name: 'value',
                        content: this.sandbox.translate('product.attribute.value')
                    },
                    {
                        name: 'attributeName',
                        content: this.sandbox.translate('product.attribute.name')
                    }
                ],
                viewOptions: {
                    table: {
                        type: 'checkbox'
                    }
                },
                data: this.options.data.attributes
            };

            this.sandbox.start([
                // start datagrid
                {
                    name: 'datagrid@husky',
                    options: datagridOptions
                }
            ]);

            this.sandbox.start([
                {
                    name: 'toolbar@husky',
                    options: {
                        el: '#product-attribute-selection-toolbar',
                        instanceName: productAttributesInstanceName,
                        small: false,
                        data: [
                            {
                                id: 'add',
                                icon: 'plus-circle',
                                callback: createAddOverlay.bind(this)
                            },
                            {
                                icon: 'trash-o',
                                disabled: false,
                                callback: attributeDelete.bind(this)
                            }
                        ]
                    }
                }
            ]);
        },

        /**
         * initialize form components
         */
        initForm = function() {
            this.sandbox.start(formSelector);
            startFormComponents.call(this);
        };

    return {
        name: 'Sulu Product Attributes View',

        templates: ['/admin/product/template/product/attributes'],

        render: function() {
            this.sandbox.dom.html(this.$el, this.renderTemplate('/admin/product/template/product/attributes'));

            initForm.call(this);

            setHeaderInformation.call(this);
        },

        initialize: function() {
            bindCustomEvents.call(this);

            this.status = !!this.options.data ? this.options.data.attributes.status : Config.get('product.status.active');

            this.render();
        }
    };
});
<commit_msg>added preSelectedElements to selectbox<commit_after>/*
 * This file is part of the Sulu CMS.
 *
 * (c) MASSIVE ART WebServices GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

define([
    'config',
    'text!suluproduct/components/products/components/attributes/overlay-content.html'
], function(Config, OverlayTpl) {
    'use strict';

    var formSelector = '#product-attributes-form',
        productAttributesInstanceName = 'product-attribute-list-toolbar',
        datagridInstanceName = 'product-attribute-datagrid',
        overlayInstanceName = 'product-attribute-overlay',
        selectInstanceName = 'product-attribute-select',
        typeText = 'product.attribute.type.text',
        attributeId = null,
        maxLengthTitle = 60,
        actions = {
            ADD: 1,
            DELETE: 2,
            UPDATE: 3
        },

        /**
         * bind custom events
         */
        bindCustomEvents = function() {
            this.sandbox.on('sulu.header.toolbar.delete', function () {
                this.sandbox.emit('sulu.product.delete', this.options.data.id);
            }.bind(this));

            this.sandbox.on('product.state.change', function(id) {
                if (!this.options.data.status || this.options.data.status.id !== id) {
                    this.status = {id: id};
                    this.options.data.status = this.status;
                    setHeaderBar.call(this, false);
                }
            }, this);

            this.sandbox.on('sulu.header.back', function() {
                this.sandbox.emit('sulu.products.list');
            }, this);

            this.sandbox.on('sulu.header.toolbar.save', function() {
                this.sendData = {};
                this.sendData.status = this.status;
                save.call(this);
            }, this);

            this.sandbox.on('sulu.products.saved', function(data) {

                var attributes = data.attributes;

                // Select action
                if (data.action === actions.ADD) {
                    // ADD RECORD IN DATAGRID
                    var result = _.findWhere(attributes, {'attributeId': data.attributeId});
                    this.sandbox.emit('husky.datagrid.' + datagridInstanceName + '.record.add', {
                        id: result.id,
                        attributeId: result.attributeId,
                        value: result.value,
                        attributeName: result.attributeName
                    });
                } else if (data.action === actions.DELETE) {
                    // DELETE RECORDs IN DATAGRID
                    $.each(data.deleteIds, function(key, id) {
                        this.sandbox.emit('husky.datagrid.' + datagridInstanceName + '.record.remove', id);
                    }.bind(this));
                } else if (data.action === actions.UPDATE) {
                    // UPDATE RECORD IN DATAGRID
                    this.sandbox.emit('husky.datagrid.' + datagridInstanceName + '.records.change', attributes);
                }

                setHeaderBar.call(this, true);
                //this.options.data = data;
                this.options.data.attributes.status = this.status;
            }, this);
        },

        /**
         * modify header information
         */
        setHeaderBar = function(saved) {
            var type = (!!this.options.data && !!this.options.data.id) ? 'edit' : 'add';
            this.sandbox.emit('sulu.header.toolbar.state.change', type, saved, true);
        },

        /**
         * Create overlay content for add attribute overlay
         */
        createAddOverlayContent = function() {
            attributeId = null;

            // create container for overlay
            var $overlayContent = this.sandbox.dom.createElement(this.sandbox.util.template(OverlayTpl, {
                translate: this.sandbox.translate
            }));
            this.sandbox.dom.append(this.$el, $overlayContent);

            return $overlayContent;
        },

        /**
         * sets the header information
         */
        setHeaderInformation = function() {
            var title = 'pim.product.title',
                breadcrumb = [
                    {title: 'navigation.pim'},
                    {title: 'pim.products.title'}
                ];
            if (!!this.options.data && !!this.options.data.attributes.name) {
                title = this.options.data.attributes.name;
            }
            title = this.sandbox.util.cropTail(title, maxLengthTitle);
            this.sandbox.emit('sulu.header.set-title', title);

            if (!!this.options.data && !!this.options.data.attributes.number) {
                breadcrumb.push({
                    title: '#' + this.options.data.attributes.number
                });
            } else {
                breadcrumb.push({
                    title: 'pim.product.title'
                });
            }
            this.sandbox.emit('sulu.header.set-breadcrumb', breadcrumb);
        },

        /**
         * Create the overlay
         */
        createAddOverlay = function() {
            // call JSON to get the attributes from the server then create the overlay after it's done
            var ajaxRequest = $.getJSON('api/attributes', function(data) {
                this.attributeTypes = new Array();

                $.each(data._embedded.attributes, function(key, value) {
                    var newAttribute = {
                        'id': value.id,
                        'name': value.name
                    };

                    // at this time we support only text type attributes
                    if (value.type.name === typeText) {
                        this.attributeTypes.push(newAttribute);
                    }
                }.bind(this));
            }.bind(this));

            ajaxRequest.done(function() {
                // create container for overlay
                var $overlay = this.sandbox.dom.createElement('<div>');
                this.sandbox.dom.append(this.$el, $overlay);

                // create content
                this.sandbox.start([
                    {
                        name: 'overlay@husky',
                        options: {
                            el: $overlay,
                            supportKeyInput: false,
                            title: this.sandbox.translate('product.attribute.overlay.title'),
                            skin: 'normal',
                            openOnStart: true,
                            removeOnClose: true,
                            instanceName: overlayInstanceName,
                            data: createAddOverlayContent.call(this),
                            okCallback: overlayOkClicked.bind(this)
                        }
                    }
                ]);

            }.bind(this));

            ajaxRequest.fail(function() {
                console.log('Error retrieving attributes from server');
            }.bind(this));

            ajaxRequest.complete(function() {

                // set pre selected element in checkbox
                if (typeof this.attributeTypes[0].name == "string" && typeof this.attributeTypes[0] == "object") {
                    attributeId = this.attributeTypes[0].id;
                    var preSelectedElement = [];
                    preSelectedElement.push(this.attributeTypes[0].name);
                }

                // create dropbox in overlay
                var selectOptions = {
                    el: '#selectBox',
                    instanceName: selectInstanceName,
                    multipleSelect: false,
                    defaultLabel: this.sandbox.translate('product.attribute.overlay.defaultlabel'),
                    preSelectedElements: preSelectedElement,
                    valueName: 'name',
                    isNative: true,
                    data: this.attributeTypes
                };

                this.sandbox.start([
                    // start select
                    {
                        name: 'select@husky',
                        options: selectOptions
                    }
                ]);

                // define select event for dropbox
                this.sandbox.on('husky.select.' + selectInstanceName + '.selected.item', function(item) {
                    attributeId = parseInt(item);
                });
            }.bind(this));
        },

        /**
         * save product attributes
         */
        save = function() {
            this.sandbox.emit('sulu.products.save', this.sendData);
        },

        /**
         * called when OK on overlay was clicked
         */
        overlayOkClicked = function() {
            // exit if no attribute is selected in overlay
            if (!attributeId) {
                return;
            }

            this.sendData = new Object();
            var attributeValue = this.sandbox.dom.val('#attribute-name');

            var attributes = this.options.data.attributes.attributes;

            var result = _.findWhere(attributes, {'attributeId': attributeId});

            if (result) {
                result.value = attributeValue;
                this.sendData.action = actions.UPDATE;
            } else {
                var newAttribute = {
                    'attributeId': attributeId,
                    'value': attributeValue
                };
                attributes.push(newAttribute);
                this.sendData.action = actions.ADD;
            }

            this.sendData.attributeId = attributeId;
            this.sendData.attributes = attributes;
            this.sendData.status = this.status;
            this.sendData.id = this.options.data.attributes.id;

            save.call(this);
        },

        /**
         * delete action function from toolbar
         */
        attributeDelete = function() {
            this.sandbox.emit('husky.datagrid.' + datagridInstanceName + '.items.get-selected', function(ids) {

                var attributes = this.options.data.attributes.attributes;
                this.sendData = new Object();
                var deleteIds = new Array();

                _.each(ids, function(value, key, list) {
                    var result = _.findWhere(attributes, {'id': value});
                    attributes = _.without(attributes, result);
                    deleteIds.push(value);
                });

                this.sendData.deleteIds = deleteIds;
                this.sendData.attributes = attributes;
                this.sendData.status = this.status;
                this.sendData.id = this.options.data.id;
                this.sendData.action = actions.DELETE;

                save.call(this);
            }.bind(this));
        },

        /**
         * calls basic form components
         */
        startFormComponents = function() {
            var datagridOptions = {
                el: '#product-attribute-list',
                instanceName: datagridInstanceName,
                resultKey: 'attributes',
                matchings: [
                    {
                        name: 'value',
                        content: this.sandbox.translate('product.attribute.value')
                    },
                    {
                        name: 'attributeName',
                        content: this.sandbox.translate('product.attribute.name')
                    }
                ],
                viewOptions: {
                    table: {
                        type: 'checkbox'
                    }
                },
                data: this.options.data.attributes
            };

            this.sandbox.start([
                // start datagrid
                {
                    name: 'datagrid@husky',
                    options: datagridOptions
                }
            ]);

            this.sandbox.start([
                {
                    name: 'toolbar@husky',
                    options: {
                        el: '#product-attribute-selection-toolbar',
                        instanceName: productAttributesInstanceName,
                        small: false,
                        data: [
                            {
                                id: 'add',
                                icon: 'plus-circle',
                                callback: createAddOverlay.bind(this)
                            },
                            {
                                icon: 'trash-o',
                                disabled: false,
                                callback: attributeDelete.bind(this)
                            }
                        ]
                    }
                }
            ]);
        },

        /**
         * initialize form components
         */
        initForm = function() {
            this.sandbox.start(formSelector);
            startFormComponents.call(this);
        };

    return {
        name: 'Sulu Product Attributes View',

        templates: ['/admin/product/template/product/attributes'],

        render: function() {
            this.sandbox.dom.html(this.$el, this.renderTemplate('/admin/product/template/product/attributes'));

            initForm.call(this);

            setHeaderInformation.call(this);
        },

        initialize: function() {
            bindCustomEvents.call(this);

            this.status = !!this.options.data ? this.options.data.attributes.status : Config.get('product.status.active');

            this.render();
        }
    };
});
<|endoftext|>


<commit_before>var Lettris = {};
Lettris.Boot = function (game) {};

Lettris.Boot.prototype = {
    preload: function () {
	// preload the loading indicator first before anything else
	this.load.image('preloaderBar', 'assets/loading-bar.png');
    },
    create: function () {
	// set scale options
	this.input.maxPointers = 1;
	this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
	this.scale.pageAlignHorizontally = true;
	this.scale.pageAlignVertically = true;

	this.game.version = "0.19 TEST2 Open Beta"

	// start the Preloader state
	this.state.start('Preloader');
    }
};
<commit_msg>0.19<commit_after>var Lettris = {};
Lettris.Boot = function (game) {};

Lettris.Boot.prototype = {
    preload: function () {
	// preload the loading indicator first before anything else
	this.load.image('preloaderBar', 'assets/loading-bar.png');
    },
    create: function () {
	// set scale options
	this.input.maxPointers = 1;
	this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
	this.scale.pageAlignHorizontally = true;
	this.scale.pageAlignVertically = true;

	this.game.version = "0.19 Open Beta"

	// start the Preloader state
	this.state.start('Preloader');
    }
};
<|endoftext|>


<commit_before>(function(console) {
	console.save = function(data, filename) {
		if (!data) {
			console.error('Console.save: No data')
			return;
		}
		if (!filename) filename = 'console.json'

		if (typeof data === "object") {
			data = JSON.stringify(data, undefined, 4)
		}

		var blob = new Blob([data], {
				type: 'text/json'
			}),
			e = document.createEvent('MouseEvents'),
			a = document.createElement('a')

		a.download = filename
		a.href = window.URL.createObjectURL(blob)
		a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
		e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
		a.dispatchEvent(e)
	}
})(console)

var vocabList = new duo.VocabList()
vocabList.fetch({
	data: {},
	success: function() {
		// console.save(vocabList, "vocabList.json")

		var vocabIndex = _.groupBy(vocabList.models, function(model) {
			return model.attributes.word_string
		})

		var words = _.map(vocabIndex, function(vocab) {
			return vocab[0].attributes.word_string
		})

		var ready = _.after(Number.parseInt(words.length/10+1), function(){
			console.save(vocabList, "vocabList.json")
		});

		for (var i = 0; i < words.length; i += 10) {
			var wordSlice = words.slice(i, i + 10);
			var tokens = JSON.stringify(wordSlice)

			$.ajax({
				url: "https://d.duolingo.com/words/hints/de/en",
				dataType: "jsonp",
				data: {
					tokens: tokens
				},
				type: "GET",
				error: function(err) {
					console.error(err);
				},
				success: function(res) {
					for (var i in res) {
						var vInd = vocabIndex[i];
						for (var j in vInd) {
							var vocab = vInd[j];
							_.extend(vocab.attributes, {
								translation: res[i]
							});
						}
					}
					console.log(res);
					ready()
				}
			})
		}
	}
})
<commit_msg>Updated script to export anki-compatible csv<commit_after>(function(console) {
	console.save = function(data, filename) {
		if (!data) {
			console.error('Console.save: No data')
			return;
		}
		if (!filename) filename = 'console.json'

		if (typeof data === "object") {
			data = JSON.stringify(data, undefined, 4)
		}

		var blob = new Blob([data], {
				type: 'text/json'
			}),
			e = document.createEvent('MouseEvents'),
			a = document.createElement('a')

		a.download = filename
		a.href = window.URL.createObjectURL(blob)
		a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
		e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
		a.dispatchEvent(e)
	}
})(console)

var vocabList = new duo.VocabList()
vocabList.fetch({
	data: {},
	success: function() {
		// console.save(vocabList, "vocabList.json")

		var vocabIndex = _.groupBy(vocabList.models, function(model) {
			return model.attributes.word_string
		})

		var words = _.map(vocabIndex, function(vocab) {
			return vocab[0].attributes.word_string
		})

		var ready = _.after(Number.parseInt(words.length / 10 + 1), function() {
			console.save(toCsv(vocabList), "vocabList.csv")
		});

		for (var i = 0; i < words.length; i += 10) {
			var wordSlice = words.slice(i, i + 10);
			var tokens = JSON.stringify(wordSlice)

			$.ajax({
				url: "https://d.duolingo.com/words/hints/de/en",
				dataType: "jsonp",
				data: {
					tokens: tokens
				},
				type: "GET",
				error: function(err) {
					console.error(err);
				},
				success: function(res) {
					for (var i in res) {
						var vInd = vocabIndex[i];
						for (var j in vInd) {
							var vocab = vInd[j];
							_.extend(vocab.attributes, {
								translation: res[i]
							});
						}
					}
					console.log(res);
					ready()
				}
			})
		}
	}
})

function toCsv(vocabList) {
	var csv = _.reduce(vocabList.models, function(memo, model) {
		var att = model.attributes;

		var translations = _.uniq(att.translation)
		translations = _.reduce(translations, function(memo, word) {
			return memo + word + ", ";
		}, "");
		translations = translations.substring(0, translations.length - 1);

		var word = att.word_string;
		if (att.gender != null) {
			word += " (" + att.gender + ")"
		}

		var tags = att.pos + " " + att.skill_url_title;

		return memo + word + "\t" + translations + "\t" + tags + "\n";
	}, "");

	return csv;
}
<|endoftext|>


<commit_before>define(["js/ui/View", "js/core/Bus"], function (View, Bus) {
    return View.inherit('sprd.view.ProductViewerClass', {

        defaults: {
            view: null,
            product: null,
            width: 300,
            height: 300,
            selectedConfiguration: null,
            editable: true,
            focused: true,

            productViewerSvg: null,
            textArea: null,
            textAreaPosition: null
        },

        inject: {
            bus: Bus
        },

        ctor: function () {
            this.callBase();
            this.bind('productViewerSvg', 'add:configurationViewer', this._onConfigurationViewerAdded, this);
            this.bind('selectedConfiguration', 'change:scale', this._positionTextArea, this);
            this.bind('selectedConfiguration', 'change:offset', this._positionTextArea, this);
        },

        _commitSelectedConfiguration: function (selectedConfiguration) {
            this._positionTextArea();
        },

        keyUp: function (e) {

            if (this.$keyPressHandled) {
                return;
            }

            // this is a work-a-round because keypress event isn't available on android
            var value = this.$.textArea.$el.value;
            if (this.$lastValue !== value && value && value.length !== 0) {
                // input
                var c = value.substr(-1),
                    viewer = this.$.selectedConfigurationViewer;

                if (c && viewer) {
                    viewer.addChar(c);
                }

            }

            this.$lastValue = value;
            this.$.textArea.$el.value = "";
        },

        keyPress: function (e) {
            this.$keyPressHandled = true;
            this.$.textArea.$el.value = "";
            this._keyPressHandler(e.domEvent);
        },

        keyDown: function (e) {
            this.$keyPressHandled = false;
            this._keyDownHandler(e.domEvent);
        },

        _positionTextArea: function () {
            try {
                var position = null,
                    selectedConfiguration = this.$.selectedConfiguration;

                if (this.$.editable && selectedConfiguration && selectedConfiguration.type === "text" && this.$.productViewerSvg && this.$.productViewerSvg.$currentProductTypeViewViewer) {
                    var factor = this.$.productViewerSvg.localToGlobalFactor(),
                        view = this.$.productViewerSvg.$currentProductTypeViewViewer.$._view,
                        viewMap;

                    for (var i = 0; i < view.$.viewMaps.$items.length; i++) {
                        if (view.$.viewMaps.$items[i].$.printArea === selectedConfiguration.$.printArea) {
                            viewMap = view.$.viewMaps.$items[i];
                            break;
                        }
                    }
                    if (viewMap) {
                        position = {
                            x: (viewMap.get("offset.x") + selectedConfiguration.get("offset.x")) * factor.x + 10,
                            y: (viewMap.get("offset.y") + selectedConfiguration.get("offset.y")) * factor.y,
                            width: selectedConfiguration.width() * factor.x - 25,
                            height: selectedConfiguration.height() * factor.y - 10
                        };
                    }

                }

                this.set("textAreaPosition", position);
            } catch (e) {
                if (this.$.bus) {
                    this.$.bus.trigger("Application.Error", e);
                } else {
                    throw e;
                }
            }
        },

        _onConfigurationViewerAdded: function (e) {
            var viewer = e.$;
            if (viewer.$.configuration === this.$.selectedConfiguration) {
                this.set('selectedConfigurationViewer', viewer);
            }
            this.trigger('add:configurationViewer', viewer);

        },

        _clickHandler: function (e) {
            if (this.$.editable && !(e.isDefaultPrevented || e.defaultPrevented) && e.domEvent.target !== this.$.textArea.$el) {
                this.set('selectedConfiguration', null);
            }
            this.set('focused', true);

            e.stopPropagation();

        },

        _commitChangedAttributes: function ($) {
            this.callBase();
            if ($ && $.hasOwnProperty('selectedConfiguration')) {
                var configuration = $['selectedConfiguration'],
                    viewer = null;
                if (!configuration) {
                    viewer = null;
                } else {
                    viewer = this.getViewerForConfiguration(configuration);
                }

                this.set('selectedConfigurationViewer', viewer);

            }
        },

        getViewerForConfiguration: function (configuration) {
            if (this.$.productViewerSvg) {
                return this.$.productViewerSvg.getViewerForConfiguration(configuration);
            }
            return null;
        },

        _keyDownHandler: function (e) {
            var self = this,
                product = self.$.product;

            var viewer = this.$.selectedConfigurationViewer;
            if (viewer) {
                viewer._keyDown(e);
                if (e.defaultPrevented) {
                    return;
                }
            }

            var selectedConfiguration = self.$.selectedConfiguration;

            if (selectedConfiguration && product) {

                var deltaX = 0,
                    deltaY = 0;

                switch (e.keyCode) {
                    case 40:
                        deltaY = 1;
                        break;
                    case 38:
                        deltaY = -1;
                        break;
                    case 37:
                        deltaX = -1;
                        break;
                    case 39:
                        deltaX = 1;
                }

                if (deltaX || deltaY) {

                    if (e.shiftKey) {
                        deltaX *= 10;
                        deltaY *= 10;
                    }

                    var offset = selectedConfiguration.$.offset;
                    offset.set({
                        x: offset.$.x + deltaX,
                        y: offset.$.y + deltaY
                    });
                    selectedConfiguration.set('offset', offset);

                    e.preventDefault();
                    e.stopPropagation();
                }

                if (e.keyCode === 8 || e.keyCode === 46) {
                    // backspace || delete --> remove selected configuration

                    product.$.configurations.remove(selectedConfiguration);
                    self.set('selectedConfiguration', null);

                    e.preventDefault();
                    e.stopPropagation();
                }
            }

        },

        _keyPressHandler: function (e) {
            var viewer = this.$.selectedConfigurationViewer;
            if (viewer) {
                viewer._keyPress(e);
            }
        },

        _bindDomEvents: function () {
            if (this.runsInBrowser() && this.$.editable) {
                var self = this;

                this.bind("on:click", this._clickHandler, this);
                this.$stage.bind('on:blur', function () {
                    self.set('focused', false);
                });
                this.$stage.bind('on:focus', function () {
                    self.set('focused', true);
                });

                this.callBase();
            }
        },

        textAreaFocused: function () {
            this.set('focused', true);

            if (this.$stage.$browser.isIOS) {
                this.$.textArea.set('visibility', 'hidden');
            }

            this.addClass("text-area-active");

        },

        textAreaBlured: function () {
            this.set('focused', false);
            if (this.$stage.$browser.isIOS) {
                this.$.textArea.set('visibility', 'visible');
            }

            this.removeClass("text-area-active");
        },

        showTextAreaOverlay: function () {
            return this.$.editable &&
                this.$.selectedConfiguration && this.$.selectedConfiguration.type === "text" &&
                this.runsInBrowser() && ('ontouchstart' in window);
        }.onChange("selectedConfiguration", "editable")
    });
});<commit_msg>added on:configurationSelect event for ProductViewer<commit_after>define(["js/ui/View", "js/core/Bus"], function (View, Bus) {
    return View.inherit('sprd.view.ProductViewerClass', {

        defaults: {
            view: null,
            product: null,
            width: 300,
            height: 300,
            selectedConfiguration: null,
            editable: true,
            focused: true,

            productViewerSvg: null,
            textArea: null,
            textAreaPosition: null
        },

        inject: {
            bus: Bus
        },

        events: ['on:configurationSelect'],

        ctor: function () {
            this.callBase();
            this.bind('productViewerSvg', 'add:configurationViewer', this._onConfigurationViewerAdded, this);
            this.bind('selectedConfiguration', 'change:scale', this._positionTextArea, this);
            this.bind('selectedConfiguration', 'change:offset', this._positionTextArea, this);
        },

        _commitSelectedConfiguration: function (selectedConfiguration) {
            this._positionTextArea();
        },

        keyUp: function (e) {

            if (this.$keyPressHandled) {
                return;
            }

            // this is a work-a-round because keypress event isn't available on android
            var value = this.$.textArea.$el.value;
            if (this.$lastValue !== value && value && value.length !== 0) {
                // input
                var c = value.substr(-1),
                    viewer = this.$.selectedConfigurationViewer;

                if (c && viewer) {
                    viewer.addChar(c);
                }

            }

            this.$lastValue = value;
            this.$.textArea.$el.value = "";
        },

        keyPress: function (e) {
            this.$keyPressHandled = true;
            this.$.textArea.$el.value = "";
            this._keyPressHandler(e.domEvent);
        },

        keyDown: function (e) {
            this.$keyPressHandled = false;
            this._keyDownHandler(e.domEvent);
        },

        _positionTextArea: function () {
            try {
                var position = null,
                    selectedConfiguration = this.$.selectedConfiguration;

                if (this.$.editable && selectedConfiguration && selectedConfiguration.type === "text" && this.$.productViewerSvg && this.$.productViewerSvg.$currentProductTypeViewViewer) {
                    var factor = this.$.productViewerSvg.localToGlobalFactor(),
                        view = this.$.productViewerSvg.$currentProductTypeViewViewer.$._view,
                        viewMap;

                    for (var i = 0; i < view.$.viewMaps.$items.length; i++) {
                        if (view.$.viewMaps.$items[i].$.printArea === selectedConfiguration.$.printArea) {
                            viewMap = view.$.viewMaps.$items[i];
                            break;
                        }
                    }
                    if (viewMap) {
                        position = {
                            x: (viewMap.get("offset.x") + selectedConfiguration.get("offset.x")) * factor.x + 10,
                            y: (viewMap.get("offset.y") + selectedConfiguration.get("offset.y")) * factor.y,
                            width: selectedConfiguration.width() * factor.x - 25,
                            height: selectedConfiguration.height() * factor.y - 10
                        };
                    }

                }

                this.set("textAreaPosition", position);
            } catch (e) {
                if (this.$.bus) {
                    this.$.bus.trigger("Application.Error", e);
                } else {
                    throw e;
                }
            }
        },

        _onConfigurationViewerAdded: function (e) {
            var viewer = e.$;
            if (viewer){
                if(viewer.$.configuration === this.$.selectedConfiguration) {
                    this.set('selectedConfigurationViewer', viewer);
                }
                this.trigger('add:configurationViewer', viewer);
            }
        },

        _clickHandler: function (e) {
            if (this.$.editable && !(e.isDefaultPrevented || e.defaultPrevented) && e.domEvent.target !== this.$.textArea.$el) {
                this.set('selectedConfiguration', null);
            }
            this.set('focused', true);

            e.stopPropagation();

        },

        _commitChangedAttributes: function ($) {
            this.callBase();
            if ($ && $.hasOwnProperty('selectedConfiguration')) {
                var configuration = $['selectedConfiguration'],
                    viewer = null;
                if (!configuration) {
                    viewer = null;
                } else {
                    viewer = this.getViewerForConfiguration(configuration);
                }
                this.trigger('on:configurationSelect', configuration);
                this.set('selectedConfigurationViewer', viewer);

            }
        },

        getViewerForConfiguration: function (configuration) {
            if (this.$.productViewerSvg) {
                return this.$.productViewerSvg.getViewerForConfiguration(configuration);
            }
            return null;
        },

        _keyDownHandler: function (e) {
            var self = this,
                product = self.$.product;

            var viewer = this.$.selectedConfigurationViewer;
            if (viewer) {
                viewer._keyDown(e);
                if (e.defaultPrevented) {
                    return;
                }
            }

            var selectedConfiguration = self.$.selectedConfiguration;

            if (selectedConfiguration && product) {

                var deltaX = 0,
                    deltaY = 0;

                switch (e.keyCode) {
                    case 40:
                        deltaY = 1;
                        break;
                    case 38:
                        deltaY = -1;
                        break;
                    case 37:
                        deltaX = -1;
                        break;
                    case 39:
                        deltaX = 1;
                }

                if (deltaX || deltaY) {

                    if (e.shiftKey) {
                        deltaX *= 10;
                        deltaY *= 10;
                    }

                    var offset = selectedConfiguration.$.offset;
                    offset.set({
                        x: offset.$.x + deltaX,
                        y: offset.$.y + deltaY
                    });
                    selectedConfiguration.set('offset', offset);

                    e.preventDefault();
                    e.stopPropagation();
                }

                if (e.keyCode === 8 || e.keyCode === 46) {
                    // backspace || delete --> remove selected configuration

                    product.$.configurations.remove(selectedConfiguration);
                    self.set('selectedConfiguration', null);

                    e.preventDefault();
                    e.stopPropagation();
                }
            }

        },

        _keyPressHandler: function (e) {
            var viewer = this.$.selectedConfigurationViewer;
            if (viewer) {
                viewer._keyPress(e);
            }
        },

        _bindDomEvents: function () {
            if (this.runsInBrowser() && this.$.editable) {
                var self = this;

                this.bind("on:click", this._clickHandler, this);
                this.$stage.bind('on:blur', function () {
                    self.set('focused', false);
                });
                this.$stage.bind('on:focus', function () {
                    self.set('focused', true);
                });

                this.callBase();
            }
        },

        textAreaFocused: function () {
            this.set('focused', true);

            if (this.$stage.$browser.isIOS) {
                this.$.textArea.set('visibility', 'hidden');
            }

            this.addClass("text-area-active");

        },

        textAreaBlured: function () {
            this.set('focused', false);
            if (this.$stage.$browser.isIOS) {
                this.$.textArea.set('visibility', 'visible');
            }

            this.removeClass("text-area-active");
        },

        showTextAreaOverlay: function () {
            return this.$.editable &&
                this.$.selectedConfiguration && this.$.selectedConfiguration.type === "text" &&
                this.runsInBrowser() && ('ontouchstart' in window);
        }.onChange("selectedConfiguration", "editable")
    });
});<|endoftext|>


<commit_before>import firebaseApp from '../api/firebase'
import { eventChannel, buffers } from 'redux-saga'
import { put, take } from 'redux-saga/effects'
import { fetchDataSuccess, fetchDataFailure } from '../actions/index'

export default function * databaseUpdate () {
  const chan = eventChannel(emit => {
    const connectionRef = firebaseApp.database().ref()
    connectionRef.on('value', snapshot => emit(snapshot.val()))
    // don't stop the channel
    return () => {}
  }, buffers.expanding(1))

  while (true) {
    let data = yield take(chan)
    if (data) {
      yield put(fetchDataSuccess(data))
    } else {
      yield put(fetchDataFailure(data))
    }
  }
}
<commit_msg>fix database update saga<commit_after>import firebaseApp from '../api/firebase'
import { eventChannel, buffers } from 'redux-saga'
import { isEmpty } from 'lodash'
import { put, take } from 'redux-saga/effects'
import { fetchDataSuccess, fetchDataFailure } from '../actions'

export default function * databaseUpdate () {
  const chan = eventChannel(emit => {
    const connectionRef = firebaseApp.database().ref()
    connectionRef.on('value', snapshot => {
      // emitted value can't be null when there are no recipes
      emit(snapshot.val() || {})
    })
    // don't stop the channel
    return () => {}
  }, buffers.expanding(1))

  while (true) {
    let data = yield take(chan)
    if (!isEmpty(data)) {
      yield put(fetchDataSuccess(data))
    } else {
      yield put(fetchDataFailure(data))
    }
  }
}
<|endoftext|>


<commit_before>"use strict";

// Minimum amount of time between updates.
var USER_INFO_UPDATE_THROTTLE = timespan.seconds(3);

// Number of historical scroll states we save.
var USER_INFO_MAX_SCROLL_STATES = 5;

function UserInfoConflict() {
}

function UserInfo(api) {
  RoostEventTarget.call(this);

  this.api_ = api;

  this.base_ = null;
  this.baseVersion_ = -1;

  this.local_ = null;
  this.pending_ = null;

  this.throttler_ =
    new Throttler(this.doUpdate_.bind(this), USER_INFO_UPDATE_THROTTLE);

  this.ready_ = Q.defer();

  // TODO(davidben): If the initial load fails, we never get the
  // ready. Retry? Maybe just query on login and save the state.
  this.loadInfo_().done();
  this.api_.addEventListener("connect", this.onConnect_.bind(this));
  this.onConnect_();
}
UserInfo.prototype = Object.create(RoostEventTarget.prototype);

UserInfo.prototype.onConnect_ = function() {
  var socket = this.api_.socket();
  if (!socket)
    return;
  socket.addEventListener("info-changed", function(ev) {
    this.handleNewInfo_(ev.info, ev.version);
  }.bind(this));
}

UserInfo.prototype.handleNewInfo_ = function(info, version) {
  // Things might have gotten reordered.
  if (this.baseVersion_ >= version)
    return;

  try {
    this.base_ = JSON.parse(info);
  } catch (err) {
    if (window.console && console.log)
      console.log("Error parsing user info", err);
    this.base_ = {};
  }

  if (typeof this.base_ !== "object" || !this.base_)
    this.base_ = {};

  this.baseVersion_ = version;
  if (Q.isPending(this.ready_.promise))
    this.ready_.resolve();
  this.dispatchEvent({type: "change"});
};

UserInfo.prototype.loadInfo_ = function() {
  return this.api_.get("/v1/info").then(function(ret) {
    this.handleNewInfo_(ret.info, ret.version);
  }.bind(this));
};

UserInfo.prototype.ready = function() {
  return this.ready_.promise;
};

UserInfo.prototype.get = function(key) {
  if (this.baseVersion_ < 0)
    throw "User info not loaded!";

  if (key === 'scrollStates') {
    return this.scrollStates();
  } else {
    if (this.local_ && key in this.local_)
      return this.local_[key];
    if (this.pending_ && key in this.pending_)
      return this.pending_[key];
    return this.base_[key];
  }
};

UserInfo.prototype.scrollStates = function() {
  var base = {
    remove: {},
    add: this.base_.scrollStates || []
  };
  if (this.pending_)
    base = mergeScrollStateDiff(base, this.pending_.scrollStates);
  if (this.local_)
    base = mergeScrollStateDiff(base, this.local_.scrollStates);
  return base.add;
}

UserInfo.prototype.ensureLocal_ = function() {
  if (this.local_)
    return;
  this.local_ = {
    scrollStates: {
      remove: {},
      add: []
    }
  };
}

UserInfo.prototype.set = function(key, value) {
  if (this.baseVersion_ < 0)
    throw "User info not loaded!";

  this.ensureLocal_();
  this.local_[key] = value;
  this.dispatchEvent({type: "change"});
  this.throttler_.request();
};

function mergeScrollStateDiff(diff, newDiff) {
  // Clone diff.
  var merged = {
    remove: {},
    add: diff.add.slice(0)
  };
  for (var id in diff.remove) {
    merged.remove[id] = 1;
  }

  // Apply removes.
  for (var id in newDiff.remove) {
    merged.remove[id] = 1;
  }
  merged.add = merged.add.filter(function(state) {
    return !newDiff.remove[state.id];
  });
  // Apply adds.
  merged.add = merged.add.concat(newDiff.add);
  // Clamp size.
  if (merged.add.length > USER_INFO_MAX_SCROLL_STATES) {
    merged.add =
      merged.add.slice(merged.add.length - USER_INFO_MAX_SCROLL_STATES);
  }
  return merged;
};

UserInfo.prototype.replaceScrollState = function(oldState, newState) {
  if (this.baseVersion_ < 0)
    throw "User info not loaded!";

  this.ensureLocal_();

  var diff = {
    remove: {},
    add: newState ? [newState] : []
  };
  if (oldState)
    diff.remove[oldState.id] = 1;

  this.local_.scrollStates = mergeScrollStateDiff(
    this.local_.scrollStates, diff);
  this.dispatchEvent({type: "change"});
  this.throttler_.request();
};

UserInfo.prototype.mergeLocalAndPending_ = function() {
  if (this.local_) {
    for (var key in this.local_) {
      if (this.local_.hasOwnProperty(key) && key != 'scrollStates')
        this.pending_[key] = this.local_[key];
    }
    this.pending_.scrollStates = mergeScrollStateDiff(
      this.pending_.scrollStates, this.local_.scrollStates);
  }

  this.local_ = this.pending_;
  this.pending_ = null;
};

UserInfo.prototype.doUpdate_ = function() {
  this.pending_ = this.local_;
  this.local_ = null;

  // Merge this into the current base.
  var newInfo = {};
  // Make a copy.
  for (var key in this.base_) {
    if (this.base_.hasOwnProperty(key))
      newInfo[key] = this.base_[key];
  }
  if (!newInfo.scrollStates)
    newInfo.scrollStates = [];

  // Apply this.pending_.
  for (var key in this.pending_) {
    if (this.pending_.hasOwnProperty(key) && key != 'scrollStates')
      newInfo[key] = this.pending_[key];
  }
  newInfo.scrollStates = mergeScrollStateDiff({
    remove: {},
    add: newInfo.scrollStates
  }, this.pending_.scrollStates).add;

  // Fire off a test-and-set.
  var newVersion = this.baseVersion_ + 1;
  var newInfoStr = JSON.stringify(newInfo);
  return this.api_.post("/v1/info", {
    expectedVersion: this.baseVersion_,
    info: newInfoStr
  }).then(function(ret) {
    if (ret.updated) {
      // Success!
      this.pending_ = null;
      this.handleNewInfo_(newInfoStr, newVersion);
    } else {
      // Failure. Retry.
      this.mergeLocalAndPending_();
      this.handleNewInfo_(ret.info, ret.version);
      // Just loop like this. It should be fine.
      return this.doUpdate_();
    }
  }.bind(this), function(err) {
    // HTTP error.
    this.mergeLocalAndPending_();
    // TODO(davidben): The throttler is bad at reporting errors! Make
    // sure this goes somewhere.
    throw err;
  }.bind(this));

};
<commit_msg>Don't pester the server for no-ops<commit_after>"use strict";

// Minimum amount of time between updates.
var USER_INFO_UPDATE_THROTTLE = timespan.seconds(3);

// Number of historical scroll states we save.
var USER_INFO_MAX_SCROLL_STATES = 5;

function UserInfoConflict() {
}

function UserInfo(api) {
  RoostEventTarget.call(this);

  this.api_ = api;

  this.base_ = null;
  this.baseVersion_ = -1;

  this.local_ = null;
  this.pending_ = null;

  this.throttler_ =
    new Throttler(this.doUpdate_.bind(this), USER_INFO_UPDATE_THROTTLE);

  this.ready_ = Q.defer();

  // TODO(davidben): If the initial load fails, we never get the
  // ready. Retry? Maybe just query on login and save the state.
  this.loadInfo_().done();
  this.api_.addEventListener("connect", this.onConnect_.bind(this));
  this.onConnect_();
}
UserInfo.prototype = Object.create(RoostEventTarget.prototype);

UserInfo.prototype.onConnect_ = function() {
  var socket = this.api_.socket();
  if (!socket)
    return;
  socket.addEventListener("info-changed", function(ev) {
    this.handleNewInfo_(ev.info, ev.version);
  }.bind(this));
}

UserInfo.prototype.handleNewInfo_ = function(info, version) {
  // Things might have gotten reordered.
  if (this.baseVersion_ >= version)
    return;

  try {
    this.base_ = JSON.parse(info);
  } catch (err) {
    if (window.console && console.log)
      console.log("Error parsing user info", err);
    this.base_ = {};
  }

  if (typeof this.base_ !== "object" || !this.base_)
    this.base_ = {};

  this.baseVersion_ = version;
  if (Q.isPending(this.ready_.promise))
    this.ready_.resolve();
  this.dispatchEvent({type: "change"});
};

UserInfo.prototype.loadInfo_ = function() {
  return this.api_.get("/v1/info").then(function(ret) {
    this.handleNewInfo_(ret.info, ret.version);
  }.bind(this));
};

UserInfo.prototype.ready = function() {
  return this.ready_.promise;
};

UserInfo.prototype.get = function(key) {
  if (this.baseVersion_ < 0)
    throw "User info not loaded!";

  if (key === 'scrollStates') {
    return this.scrollStates();
  } else {
    if (this.local_ && key in this.local_)
      return this.local_[key];
    if (this.pending_ && key in this.pending_)
      return this.pending_[key];
    return this.base_[key];
  }
};

UserInfo.prototype.scrollStates = function() {
  var base = {
    remove: {},
    add: this.base_.scrollStates || []
  };
  if (this.pending_)
    base = mergeScrollStateDiff(base, this.pending_.scrollStates);
  if (this.local_)
    base = mergeScrollStateDiff(base, this.local_.scrollStates);
  return base.add;
}

UserInfo.prototype.ensureLocal_ = function() {
  if (this.local_)
    return;
  this.local_ = {
    scrollStates: {
      remove: {},
      add: []
    }
  };
}

UserInfo.prototype.set = function(key, value) {
  if (this.baseVersion_ < 0)
    throw "User info not loaded!";

  this.ensureLocal_();
  this.local_[key] = value;
  this.dispatchEvent({type: "change"});
  this.throttler_.request();
};

function mergeScrollStateDiff(diff, newDiff) {
  // Clone diff.
  var merged = {
    remove: {},
    add: diff.add.slice(0)
  };
  for (var id in diff.remove) {
    merged.remove[id] = 1;
  }

  // Apply removes.
  for (var id in newDiff.remove) {
    merged.remove[id] = 1;
  }
  merged.add = merged.add.filter(function(state) {
    return !newDiff.remove[state.id];
  });
  // Apply adds.
  merged.add = merged.add.concat(newDiff.add);
  // Clamp size.
  if (merged.add.length > USER_INFO_MAX_SCROLL_STATES) {
    merged.add =
      merged.add.slice(merged.add.length - USER_INFO_MAX_SCROLL_STATES);
  }
  return merged;
};

UserInfo.prototype.replaceScrollState = function(oldState, newState) {
  if (this.baseVersion_ < 0)
    throw "User info not loaded!";

  this.ensureLocal_();

  var diff = {
    remove: {},
    add: newState ? [newState] : []
  };
  if (oldState)
    diff.remove[oldState.id] = 1;

  this.local_.scrollStates = mergeScrollStateDiff(
    this.local_.scrollStates, diff);
  this.dispatchEvent({type: "change"});
  this.throttler_.request();
};

UserInfo.prototype.mergeLocalAndPending_ = function() {
  if (this.local_) {
    for (var key in this.local_) {
      if (this.local_.hasOwnProperty(key) && key != 'scrollStates')
        this.pending_[key] = this.local_[key];
    }
    this.pending_.scrollStates = mergeScrollStateDiff(
      this.pending_.scrollStates, this.local_.scrollStates);
  }

  this.local_ = this.pending_;
  this.pending_ = null;
};

UserInfo.prototype.doUpdate_ = function() {
  this.pending_ = this.local_;
  this.local_ = null;

  // Merge this into the current base.
  var newInfo = {};
  // Make a copy.
  for (var key in this.base_) {
    if (this.base_.hasOwnProperty(key))
      newInfo[key] = this.base_[key];
  }
  if (!newInfo.scrollStates)
    newInfo.scrollStates = [];

  // Apply this.pending_.
  for (var key in this.pending_) {
    if (this.pending_.hasOwnProperty(key) && key != 'scrollStates')
      newInfo[key] = this.pending_[key];
  }
  newInfo.scrollStates = mergeScrollStateDiff({
    remove: {},
    add: newInfo.scrollStates
  }, this.pending_.scrollStates).add;

  var newVersion = this.baseVersion_ + 1;
  var newInfoStr = JSON.stringify(newInfo);

  var oldInfoStr = JSON.stringify(this.base_);
  if (oldInfoStr === newInfoStr) {
    // Nothing changed. Don't bother.
    this.pending_ = null;
    return;
  }

  // Fire off a test-and-set.
  return this.api_.post("/v1/info", {
    expectedVersion: this.baseVersion_,
    info: newInfoStr
  }).then(function(ret) {
    if (ret.updated) {
      // Success!
      this.pending_ = null;
      this.handleNewInfo_(newInfoStr, newVersion);
    } else {
      // Failure. Retry.
      this.mergeLocalAndPending_();
      this.handleNewInfo_(ret.info, ret.version);
      // Just loop like this. It should be fine.
      return this.doUpdate_();
    }
  }.bind(this), function(err) {
    // HTTP error.
    this.mergeLocalAndPending_();
    // TODO(davidben): The throttler is bad at reporting errors! Make
    // sure this goes somewhere.
    throw err;
  }.bind(this));

};
<|endoftext|>


<commit_before>#!/usr/bin/env node
var Runner = require("../lib/runner");

var r = new Runner();

require('whereis')('java', function searched(err) {
  if (err) {
    return console.error(
      'Could not find java, make sure it is installed in your $PATH');
  }
});

r.run(function() {
  console.log("Finished callback");
});
<commit_msg>don't run if Java is missing<commit_after>#!/usr/bin/env node
var Runner = require("../lib/runner");

var r = new Runner();

require('whereis')('java', function searched(err) {
  // yep, we still need Java for the crawler & browsertime
  if (err) {
    console.error(
      'Could not find Java, make sure it is installed in your $PATH');
  }
  else {
    r.run(function() {
      console.log("Finished callback");
    });
  }
});
<|endoftext|>