<commit_before>/* Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others.
   You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
   either express or implied. See the License for the specific language governing permissions and limitations under the License.*/

define.class(function($ui$, view){

	this.attributes = {
		// Color of the draggable part of the scrollbar
		draggercolor: Config({type: vec4, value: vec4("#808080")}),

		// Color of the draggable part of the scrollbar
		draggerradius: Config({type: float, value: 3}),

		// Color when the pointer is hovering over the draggable part of the scrollbar
		hovercolor: Config({type: vec4, value: vec4("#707070")}),

		// Color of the draggable part of the scrollbar while actively scrolling
		activecolor: Config({type: vec4, value: vec4("#808080")}),

		// Is this a horizontal or a vertical scrollbar?
		vertical: Config({type: Boolean, value: true}),

		// Current value of the scrollbar. Ranges from 0 to total - page
		value: Config({type:float, value:0}),

		// Page size, in total
		page: Config({type:float, value:0}),

		// total size.
		total: Config({type:float, value:0}),

		// set animation on bgcolor
		bgcolor: Config({duration: 1.0})
	}

	this.noscroll = true

	var scrollbar = this.constructor;

	this.page = function(){
		this.redraw()
	}

	this.offset = function(){
		this.redraw()
	}

	var mesh = vec2.array()
	mesh.pushQuad(0,0,0,1,1,0,1,1)

	this.bg = {
		draggercolor: vec4(),
		offset: 0,
		page: 0.3,
		color: function(){
			// we have a rectangle
			var rel = vec2(mesh.x*view.layout.width, mesh.y*view.layout.height)
			var offset = view.value / view.total
			var page = view.page / view.total
			var edge = 0.1//min(length(vec2(length(dFdx(rel)), length(dFdy(rel)))) * SQRT_1_2, 0.001)
			var field = float(0)
			if(view.vertical){
				field = shape.roundbox(rel, 0.05 * view.layout.width, offset*view.layout.height,.9*view.layout.width, page*view.layout.height, view.draggerradius)
			}
			else{
				field = shape.roundbox(rel, offset * view.layout.width, 0.05*view.layout.height,page*view.layout.width, .9*view.layout.height, view.draggerradius)
			}
			var fg = vec4(view.draggercolor.rgb, smoothstep(edge, -edge, field)*view.draggercolor.a)
			var bg = vec4(0.,0.,0.,0.05)
			return mix(bg.rgba, fg.rgba, fg.a)
		},
		mesh: mesh,
		update:function(){},
		position: function(){
			return vec4(mesh.x * view.layout.width, mesh.y * view.layout.height, 0, 1) * view.totalmatrix * view.viewmatrix
		}
	}

	this.margin = 1

	this.vertical = function(){
		if (this.vertical){
			this.cursor = "ns-resize";
		}else{
			this.cursor = "ew-resize";
		}
	}

	this.pointermove = function(event){
		var page = this.page / this.total
		var delta = event.value[0].delta
		if(this.vertical){
			var p = delta[1] / this.layout.height
		}
		else{
			var p = delta[0] / this.layout.width
		}
		var value = clamp(p, 0, 1.-page) * this.total
		if(value != this.value){
			this.value = value
		}
	}

	this.drawcount = 0;

	this.constructor.examples = {
		Usage:function(){
			return [scrollbar({vertical: false, height: 20, total: 1, page: 0.2, offset: 0.5})]
		}
	}
})
<commit_msg>Fixed scrollbar reset bug.<commit_after>/* Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others.
   You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
   either express or implied. See the License for the specific language governing permissions and limitations under the License.*/

define.class(function($ui$, view){

	this.attributes = {
		// Color of the draggable part of the scrollbar
		draggercolor: Config({type: vec4, value: vec4("#808080")}),

		// Color of the draggable part of the scrollbar
		draggerradius: Config({type: float, value: 3}),

		// Color when the pointer is hovering over the draggable part of the scrollbar
		hovercolor: Config({type: vec4, value: vec4("#707070")}),

		// Color of the draggable part of the scrollbar while actively scrolling
		activecolor: Config({type: vec4, value: vec4("#808080")}),

		// Is this a horizontal or a vertical scrollbar?
		vertical: Config({type: Boolean, value: true}),

		// Current value of the scrollbar. Ranges from 0 to total - page
		value: Config({type:float, value:0}),

		// Page size, in total
		page: Config({type:float, value:0}),

		// total size.
		total: Config({type:float, value:0}),

		// set animation on bgcolor
		bgcolor: Config({duration: 1.0})
	}

	this.noscroll = true

	var scrollbar = this.constructor;

	this.page = function(){
		this.redraw()
	}

	this.offset = function(){
		this.redraw()
	}

	var mesh = vec2.array()
	mesh.pushQuad(0,0,0,1,1,0,1,1)

	this.bg = {
		draggercolor: vec4(),
		offset: 0,
		page: 0.3,
		color: function(){
			// we have a rectangle
			var rel = vec2(mesh.x*view.layout.width, mesh.y*view.layout.height)
			var offset = view.value / view.total
			var page = view.page / view.total
			var edge = 0.1//min(length(vec2(length(dFdx(rel)), length(dFdy(rel)))) * SQRT_1_2, 0.001)
			var field = float(0)
			if(view.vertical){
				field = shape.roundbox(rel, 0.05 * view.layout.width, offset*view.layout.height,.9*view.layout.width, page*view.layout.height, view.draggerradius)
			}
			else{
				field = shape.roundbox(rel, offset * view.layout.width, 0.05*view.layout.height,page*view.layout.width, .9*view.layout.height, view.draggerradius)
			}
			var fg = vec4(view.draggercolor.rgb, smoothstep(edge, -edge, field)*view.draggercolor.a)
			var bg = vec4(0.,0.,0.,0.05)
			return mix(bg.rgba, fg.rgba, fg.a)
		},
		mesh: mesh,
		update:function(){},
		position: function(){
			return vec4(mesh.x * view.layout.width, mesh.y * view.layout.height, 0, 1) * view.totalmatrix * view.viewmatrix
		}
	}

	this.margin = 1

	this.vertical = function(){
		if (this.vertical){
			this.cursor = "ns-resize";
		}else{
			this.cursor = "ew-resize";
		}
	}

	this.pointermove = function(event){
		var offset = this.value / this.total
		var page = this.page / this.total
		var movement = event.value[0].movement
		if (this.vertical){
			var p = offset + movement[1] / this.layout.height
		} else {
			var p = offset + movement[0] / this.layout.width
		}
		var value = clamp(p, 0, 1 - page) * this.total
		if(value != this.value){
			this.value = value
		}
	}

	this.drawcount = 0;

	this.constructor.examples = {
		Usage:function(){
			return [scrollbar({vertical: false, height: 20, total: 1, page: 0.2, offset: 0.5})]
		}
	}
})
<|endoftext|>


<commit_before>import tv4 from 'tv4';

tv4.addFormat('date-time', (data) => {
    if (typeof data === 'string') {
        data = new Date(data);
    }
    if ((data instanceof Date) && !isNaN(data.getTime())) {
        return null;
    }
    return "Invalid date";
});

function isObject(val) {
    return val !== null && val !== undefined && typeof val === 'object';
}

/**
 * Clone javascript objects.
 * @private
 *
 * @param {Object|Array} obj The object to clone.
 * @param {Function} callback An optional function which runs before inserting a property.
 * @return {Object|Array} The clone of the object.
 */
function clone(obj, callback) {
    callback = callback || function(scope, key, prop) { return prop; };
    if (Array.isArray(obj)) {
        return obj.map((entry, index) => {
            entry = callback(obj, index, entry);
            if (isObject(entry)) {
                return clone(entry, callback);
            }
            return entry;
        });
    } else if (obj instanceof Date) {
        return new Date(obj);
    }
    let res = {};
    Object.keys(obj).forEach((k) => {
        let val = callback(obj, k, obj[k]);
        if (isObject(val)) {
            res[k] = clone(val, callback);
        } else {
            res[k] = val;
        }
    });
    return res;
}
/**
 * Merge two objects in a new one.
 * @private
 *
 * @param {Object} obj1 The initial object.
 * @param {Object} obj2 The object to merge.
 * @return {Object} The merged object.
 */
function merge(obj1, obj2) {
    let res = clone(obj1);
    Object.keys(obj2).forEach((key) => {
        if (isObject(obj2[key])) {
            if (isObject(res[key])) {
                res[key] = merge(res[key], obj2[key]);
            } else {
                res[key] = obj2[key];
            }
        } else {
            res[key] = obj2[key];
        }
    });
    return res;
}
/**
 * Get data from an object.
 * @private
 *
 * @param {Object} scope The object to use.
 * @param {String} k The data key.
 * @param {Boolean} internal Should get value has private property.
 * @return {any} The value of the object for the given key.
 */
function get(scope, k, internal) {
    if (internal) {
        k = getSymbol(k);
    }
    return scope[k];
}
/**
 * Merge data to another object.
 * @private
 *
 * @param {Object} scope The object to update.
 * @param {Object} data The object to merge.
 * @param {Boolean} internal Should set value has private property.
 */
function set(scope, data, internal) {
    Object.keys(data).forEach((k) => {
        let ok = k;
        if (internal) {
            k = getSymbol(k);
        }
        if (scope[k] !== data[ok]) {
            scope[k] = data[ok];
        }
    });
}
/**
 * Create a private symbol.
 * @private
 *
 * @param {String} name The name of the property to privatize.
 * @return {Symbol|String}
 */
function getSymbol(name) {
    if (!getSymbol.cache[name]) {
        if (typeof Symbol !== 'undefined') {
            getSymbol.cache[name] = Symbol(name);
        } else {
            getSymbol.cache[name] = `__${name}`;
        }
    }
    return getSymbol.cache[name];
}

getSymbol.cache = {};
/**
 * Get all root properties merging definition.
 * @private
 *
 * @param {Object} schema The schema to analyze.
 * @param {Object} validator The validator instance.
 * @return {Object} A list of properties.
 */
function getProperties(schema, validator) {
    let root = !validator;
    if (root) {
        validator = tv4.freshApi();
        validator.addSchema('', schema);
    }
    if (schema.$ref) {
        schema = validator.getSchema(schema.$ref);
    }
    if (schema.properties) {
        return clone(schema.properties);
    } else if (root) {
        let res = {};
        let defs = schema['anyOf'] || schema['allOf'] || schema['oneOf'];
        if (defs) {
            defs.forEach((def) => {
                res = merge(res, getProperties(def, validator));
            });
        }
        return res;
    }
    return {};
}

const DEFAULT_OPTIONS = {
    validate: true,
    internal: false,
};

export class SchemaModel {
    /**
     * Merge two objects in a new one.
     *
     * @param {Object} obj1 The initial object.
     * @param {Object} obj2 The object to merge.
     * @return {Object} The merged object.
     */
    static merge(...args) {
        return merge(...args);
    }
    /**
     * Clone javascript objects.
     *
     * @param {Object|Array} obj The object to clone.
     * @param {Function} callback An optional function which runs before inserting a property.
     * @return {Object|Array} The clone of the object.
     */
    static clone(...args) {
        return clone(...args);
    }
    /**
     * Create a new schema class extending SchemaModel.
     *
     * @param {Object} schema The schema to use for the new model class.
     * @return {class} An extended SchemaModel.
     */
    static create(schema) {
        return class extends SchemaModel {
            static get schema() {
                return schema;
            }
        };
    }
    /**
     * The schema of the model.
     * @type {Object}
     * @memberof SchemaModel
     */
    static get schema() {
        throw new Error('Missing schema');
    }
    /**
     * The schema merged properties.
     * @type {Object}
     * @memberof SchemaModel
     */
    static get schemaProperties() {
        return getProperties(this.schema);
    }
    /**
     * Generate Model classes based on JSON Schema definition.
     * @class
     *
     * @param {Object} data The (optional) initial data to set.
     * @param {Object} options Optional options for data setting.
     */
    constructor(data, options) {
        if (data) {
            this.set(data, options);
        }
    }
    /**
     * Get a property value.
     *
     * @param {String} name The property name to retrieve.
     * @param {Object} options Optional options for data getting.
     * @return {any} The property value.
     */
    get(name, options) {
        options = merge(DEFAULT_OPTIONS, options || {});
        return get(this, name, options.internal);
    }
    /**
     * Set a bunch of properties.
     *
     * @param {Object} data The data to set.
     * @param {Object} options Optional options for data setting.
     */
    set(data, value, options) {
        if (typeof data === 'string') {
            return this.set({
                [data]: value,
            }, options);
        }
        data = data || {};
        options = merge(DEFAULT_OPTIONS, value || {});
        if (!options.internal && options.validate) {
            let dataToValidate = merge(this.toJSON(true), data);
            let res = this.validate(dataToValidate, options);
            /* istanbul ignore if */
            if (!res.valid) {
                if (res.error && res.error.message) {
                    throw new Error(res.error.message);
                } else if (res.missing.length) {
                    throw new Error(`Missing $ref schemas: ${res.missing.join(', ')}`);
                }
                throw new Error('Validation failed');
            }
        }
        set(this, data, options.internal);
    }
    /**
     * Validate a bunch of data or the model instance.
     *
     * @param {Object} data Optional data to validate (if empty, use model's data).
     * @return {Object} A validation result.
     */
    validate(data) {
        data = data || this.toJSON(true);
        let schema = this.constructor.schema;
        let res = tv4.validateResult(data, schema);
        /* istanbul ignore if  */
        if (res.valid && res.missing.length) {
            res.valid = false;
        }
        return res;
    }
    /**
     * Convert the model to a plain javascript object.
     *
     * @param {Boolean} stripUndefined Strip undefined values from model.
     * @return {Object} A representation of the model as plain object.
     */
    toJSON(stripUndefined) {
        let keys = Object.keys(this.constructor.schemaProperties);
        let res = {};
        keys.forEach((key) => {
            let val = this.get(key);
            if (!stripUndefined || val !== undefined) {
                res[key] = val;
            }
        });
        res = clone(res, (scope, key, prop) => {
            if (prop instanceof SchemaModel) {
                return prop.toJSON(stripUndefined);
            }
            return prop;
        });
        return res;
    }
}

SchemaModel.symbols = {};
<commit_msg>fix: improve `schemaProperties` getter<commit_after>import tv4 from 'tv4';

tv4.addFormat('date-time', (data) => {
    if (typeof data === 'string') {
        data = new Date(data);
    }
    if ((data instanceof Date) && !isNaN(data.getTime())) {
        return null;
    }
    return "Invalid date";
});

function isObject(val) {
    return val !== null && val !== undefined && typeof val === 'object';
}

/**
 * Clone javascript objects.
 * @private
 *
 * @param {Object|Array} obj The object to clone.
 * @param {Function} callback An optional function which runs before inserting a property.
 * @return {Object|Array} The clone of the object.
 */
function clone(obj, callback) {
    callback = callback || function(scope, key, prop) { return prop; };
    if (Array.isArray(obj)) {
        return obj.map((entry, index) => {
            entry = callback(obj, index, entry);
            if (isObject(entry)) {
                return clone(entry, callback);
            }
            return entry;
        });
    } else if (obj instanceof Date) {
        return new Date(obj);
    }
    let res = {};
    Object.keys(obj).forEach((k) => {
        let val = callback(obj, k, obj[k]);
        if (isObject(val)) {
            res[k] = clone(val, callback);
        } else {
            res[k] = val;
        }
    });
    return res;
}
/**
 * Merge two objects in a new one.
 * @private
 *
 * @param {Object} obj1 The initial object.
 * @param {Object} obj2 The object to merge.
 * @return {Object} The merged object.
 */
function merge(obj1, obj2) {
    let res = clone(obj1);
    Object.keys(obj2).forEach((key) => {
        if (isObject(obj2[key])) {
            if (isObject(res[key])) {
                res[key] = merge(res[key], obj2[key]);
            } else {
                res[key] = obj2[key];
            }
        } else {
            res[key] = obj2[key];
        }
    });
    return res;
}
/**
 * Get data from an object.
 * @private
 *
 * @param {Object} scope The object to use.
 * @param {String} k The data key.
 * @param {Boolean} internal Should get value has private property.
 * @return {any} The value of the object for the given key.
 */
function get(scope, k, internal) {
    if (internal) {
        k = getSymbol(k);
    }
    return scope[k];
}
/**
 * Merge data to another object.
 * @private
 *
 * @param {Object} scope The object to update.
 * @param {Object} data The object to merge.
 * @param {Boolean} internal Should set value has private property.
 */
function set(scope, data, internal) {
    Object.keys(data).forEach((k) => {
        let ok = k;
        if (internal) {
            k = getSymbol(k);
        }
        if (scope[k] !== data[ok]) {
            scope[k] = data[ok];
        }
    });
}
/**
 * Create a private symbol.
 * @private
 *
 * @param {String} name The name of the property to privatize.
 * @return {Symbol|String}
 */
function getSymbol(name) {
    if (!getSymbol.cache[name]) {
        if (typeof Symbol !== 'undefined') {
            getSymbol.cache[name] = Symbol(name);
        } else {
            getSymbol.cache[name] = `__${name}`;
        }
    }
    return getSymbol.cache[name];
}

getSymbol.cache = {};
/**
 * Get all root properties merging definition.
 * @private
 *
 * @param {Object} schema The schema to analyze.
 * @param {Object} validator The validator instance.
 * @return {Object} A list of properties.
 */
function getProperties(schema, validator) {
    let root = !validator;
    if (root) {
        validator = tv4.freshApi();
        validator.addSchema('', schema);
    }
    if (schema.definitions) {
        for (let k in schema.definitions) {
            validator.addSchema(`#/definitions/${k}`, schema.definitions[k]);
        }
    }
    if (schema.$ref) {
        schema = validator.getSchema(schema.$ref);
    }
    if (schema.properties) {
        return clone(schema.properties);
    } else {
        let res = {};
        let defs = schema['anyOf'] || schema['allOf'] || (root && schema['oneOf']);
        if (defs) {
            defs.forEach((def) => {
                res = merge(res, getProperties(def, validator));
            });
        }
        return res;
    }
    return {};
}

const DEFAULT_OPTIONS = {
    validate: true,
    internal: false,
};

export class SchemaModel {
    /**
     * Merge two objects in a new one.
     *
     * @param {Object} obj1 The initial object.
     * @param {Object} obj2 The object to merge.
     * @return {Object} The merged object.
     */
    static merge(...args) {
        return merge(...args);
    }
    /**
     * Clone javascript objects.
     *
     * @param {Object|Array} obj The object to clone.
     * @param {Function} callback An optional function which runs before inserting a property.
     * @return {Object|Array} The clone of the object.
     */
    static clone(...args) {
        return clone(...args);
    }
    /**
     * Create a new schema class extending SchemaModel.
     *
     * @param {Object} schema The schema to use for the new model class.
     * @return {class} An extended SchemaModel.
     */
    static create(schema) {
        return class extends SchemaModel {
            static get schema() {
                return schema;
            }
        };
    }
    /**
     * The schema of the model.
     * @type {Object}
     * @memberof SchemaModel
     */
    static get schema() {
        throw new Error('Missing schema');
    }
    /**
     * The schema merged properties.
     * @type {Object}
     * @memberof SchemaModel
     */
    static get schemaProperties() {
        return getProperties(this.schema);
    }
    /**
     * Generate Model classes based on JSON Schema definition.
     * @class
     *
     * @param {Object} data The (optional) initial data to set.
     * @param {Object} options Optional options for data setting.
     */
    constructor(data, options) {
        if (data) {
            this.set(data, options);
        }
    }
    /**
     * Get a property value.
     *
     * @param {String} name The property name to retrieve.
     * @param {Object} options Optional options for data getting.
     * @return {any} The property value.
     */
    get(name, options) {
        options = merge(DEFAULT_OPTIONS, options || {});
        return get(this, name, options.internal);
    }
    /**
     * Set a bunch of properties.
     *
     * @param {Object} data The data to set.
     * @param {Object} options Optional options for data setting.
     */
    set(data, value, options) {
        if (typeof data === 'string') {
            return this.set({
                [data]: value,
            }, options);
        }
        data = data || {};
        options = merge(DEFAULT_OPTIONS, value || {});
        if (!options.internal && options.validate) {
            let dataToValidate = merge(this.toJSON(true), data);
            let res = this.validate(dataToValidate, options);
            /* istanbul ignore if */
            if (!res.valid) {
                if (res.error && res.error.message) {
                    throw new Error(res.error.message);
                } else if (res.missing.length) {
                    throw new Error(`Missing $ref schemas: ${res.missing.join(', ')}`);
                }
                throw new Error('Validation failed');
            }
        }
        set(this, data, options.internal);
    }
    /**
     * Validate a bunch of data or the model instance.
     *
     * @param {Object} data Optional data to validate (if empty, use model's data).
     * @return {Object} A validation result.
     */
    validate(data) {
        data = data || this.toJSON(true);
        let schema = this.constructor.schema;
        let res = tv4.validateResult(data, schema);
        /* istanbul ignore if  */
        if (res.valid && res.missing.length) {
            res.valid = false;
        }
        return res;
    }
    /**
     * Convert the model to a plain javascript object.
     *
     * @param {Boolean} stripUndefined Strip undefined values from model.
     * @return {Object} A representation of the model as plain object.
     */
    toJSON(stripUndefined) {
        let keys = Object.keys(this.constructor.schemaProperties);
        let res = {};
        keys.forEach((key) => {
            let val = this.get(key);
            if (!stripUndefined || val !== undefined) {
                res[key] = val;
            }
        });
        res = clone(res, (scope, key, prop) => {
            if (prop instanceof SchemaModel) {
                return prop.toJSON(stripUndefined);
            }
            return prop;
        });
        return res;
    }
}

SchemaModel.symbols = {};
<|endoftext|>


<commit_before>/* jshint -W101 */
//var nouns = [
//];
var pluralNouns = [
    "Aliens", "Animals", "Antelopes", "Ants", "Apes", "Apples", "Baboons", "Bacteria", "Badgers", "Bananas", "Bats",
    "Bears", "Birds", "Bonobos", "Brides", "Bugs", "Bulls", "Butterflies", "Cheetahs",
    "Cherries", "Chicken", "Children", "Chimps", "Clowns", "Cows", "Creatures", "Dinosaurs", "Dogs", "Dolphins",
    "Donkeys", "Dragons", "Ducks", "Dwarfs", "Eagles", "Elephants", "Elves", "FAIL", "Fathers",
    "Fish", "Flowers", "Frogs", "Fruit", "Fungi", "Galaxies", "Geese", "Goats",
    "Gorillas", "Hedgehogs", "Hippos", "Horses", "Hunters", "Insects", "Kids", "Knights",
    "Lemons", "Lemurs", "Leopards", "LifeForms", "Lions", "Lizards", "Mice", "Monkeys", "Monsters",
    "Mushrooms", "Octopodes", "Oranges", "Orangutans", "Organisms", "Pants", "Parrots", "Penguins",
    "People", "Pigeons", "Pigs", "Pineapples", "Plants", "Potatoes", "Priests", "Rats", "Reptiles", "Reptilians",
    "Rhinos", "Seagulls", "Sheep", "Siblings", "Snakes", "Spaghetti", "Spiders", "Squid", "Squirrels",
    "Stars", "Students", "Teachers", "Tigers", "Tomatoes", "Trees", "Vampires", "Vegetables", "Viruses", "Vulcans",
    "Warewolves", "Weasels", "Whales", "Witches", "Wizards", "Wolves", "Workers", "Worms", "Zebras"
];
//var places = [
//"Pub", "University", "Airport", "Library", "Mall", "Theater", "Stadium", "Office", "Show", "Gallows", "Beach",
// "Cemetery", "Hospital", "Reception", "Restaurant", "Bar", "Church", "House", "School", "Square", "Village",
// "Cinema", "Movies", "Party", "Restroom", "End", "Jail", "PostOffice", "Station", "Circus", "Gates", "Entrance",
// "Bridge"
//];
var verbs = [
    "Abandon", "Adapt", "Advertise", "Answer", "Anticipate", "Appreciate",
    "Approach", "Argue", "Ask", "Bite", "Blossom", "Blush", "Breathe", "Breed", "Bribe", "Burn", "Calculate",
    "Clean", "Code", "Communicate", "Compute", "Confess", "Confiscate", "Conjugate", "Conjure", "Consume",
    "Contemplate", "Crawl", "Dance", "Delegate", "Devour", "Develop", "Differ", "Discuss",
    "Dissolve", "Drink", "Eat", "Elaborate", "Emancipate", "Estimate", "Expire", "Extinguish",
    "Extract", "FAIL", "Facilitate", "Fall", "Feed", "Finish", "Floss", "Fly", "Follow", "Fragment", "Freeze",
    "Gather", "Glow", "Grow", "Hex", "Hide", "Hug", "Hurry", "Improve", "Intersect", "Investigate", "Jinx",
    "Joke", "Jubilate", "Kiss", "Laugh", "Manage", "Meet", "Merge", "Move", "Object", "Observe", "Offer",
    "Paint", "Participate", "Party", "Perform", "Plan", "Pursue", "Pierce", "Play", "Postpone", "Pray", "Proclaim",
    "Question", "Read", "Reckon", "Rejoice", "Represent", "Resize", "Rhyme", "Scream", "Search", "Select", "Share", "Shoot",
    "Shout", "Signal", "Sing", "Skate", "Sleep", "Smile", "Smoke", "Solve", "Spell", "Steer", "Stink",
    "Substitute", "Swim", "Taste", "Teach", "Terminate", "Think", "Type", "Unite", "Vanish", "Worship"
];
var adverbs = [
    "Absently", "Accurately", "Accusingly", "Adorably", "AllTheTime", "Alone", "Always", "Amazingly", "Angrily",
    "Anxiously", "Anywhere", "Appallingly", "Apparently", "Articulately", "Astonishingly", "Badly", "Barely",
    "Beautifully", "Blindly", "Bravely", "Brightly", "Briskly", "Brutally", "Calmly", "Carefully", "Casually",
    "Cautiously", "Cleverly", "Constantly", "Correctly", "Crazily", "Curiously", "Cynically", "Daily",
    "Dangerously", "Deliberately", "Delicately", "Desperately", "Discreetly", "Eagerly", "Easily", "Euphoricly",
    "Evenly", "Everywhere", "Exactly", "Expectantly", "Extensively", "FAIL", "Ferociously", "Fiercely", "Finely",
    "Flatly", "Frequently", "Frighteningly", "Gently", "Gloriously", "Grimly", "Guiltily", "Happily",
    "Hard", "Hastily", "Heroically", "High", "Highly", "Hourly", "Humbly", "Hysterically", "Immensely",
    "Impartially", "Impolitely", "Indifferently", "Intensely", "Jealously", "Jovially", "Kindly", "Lazily",
    "Lightly", "Loudly", "Lovingly", "Loyally", "Magnificently", "Malevolently", "Merrily", "Mightily", "Miserably",
    "Mysteriously", "NOT", "Nervously", "Nicely", "Nowhere", "Objectively", "Obnoxiously", "Obsessively",
    "Obviously", "Often", "Painfully", "Patiently", "Playfully", "Politely", "Poorly", "Precisely", "Promptly",
    "Quickly", "Quietly", "Randomly", "Rapidly", "Rarely", "Recklessly", "Regularly", "Remorsefully", "Responsibly",
    "Rudely", "Ruthlessly", "Sadly", "Scornfully", "Seamlessly", "Seldom", "Selfishly", "Seriously", "Shakily",
    "Sharply", "Sideways", "Silently", "Sleepily", "Slightly", "Slowly", "Slyly", "Smoothly", "Softly", "Solemnly",
    "Steadily", "Sternly", "Strangely", "Strongly", "Stunningly", "Surely", "Tenderly", "Thoughtfully",
    "Tightly", "Uneasily", "Vanishingly", "Violently", "Warmly", "Weakly", "Wearily", "Weekly", "Weirdly", "Well",
    "Well", "Wickedly", "Wildly", "Wisely", "Wonderfully", "Yearly"
];
var adjectives = [
    "Abominable", "Accurate", "Adorable", "All", "Alleged", "Ancient", "Angry", "Angry", "Anxious", "Appalling",
    "Apparent", "Astonishing", "Attractive", "Awesome", "Baby", "Bad", "Beautiful", "Benign", "Big", "Bitter",
    "Blind", "Blue", "Bold", "Brave", "Bright", "Brisk", "Calm", "Camouflaged", "Casual", "Cautious",
    "Choppy", "Chosen", "Clever", "Cold", "Cool", "Crawly", "Crazy", "Creepy", "Cruel", "Curious", "Cynical",
    "Dangerous", "Dark", "Delicate", "Desperate", "Difficult", "Discreet", "Disguised", "Dizzy",
    "Dumb", "Eager", "Easy", "Edgy", "Electric", "Elegant", "Emancipated", "Enormous", "Euphoric", "Evil",
    "FAIL", "Fast", "Ferocious", "Fierce", "Fine", "Flawed", "Flying", "Foolish", "Foxy",
    "Freezing", "Funny", "Furious", "Gentle", "Glorious", "Golden", "Good", "Green", "Green", "Guilty",
    "Hairy", "Happy", "Hard", "Hasty", "Hazy", "Heroic", "Hostile", "Hot", "Humble", "Humongous",
    "Humorous", "Hysterical", "Idealistic", "Ignorant", "Immense", "Impartial", "Impolite", "Indifferent",
    "Infuriated", "Insightful", "Intense", "Interesting", "Intimidated", "Intriguing", "Jealous", "Jolly", "Jovial",
    "Jumpy", "Kind", "Laughing", "Lazy", "Liquid", "Lonely", "Longing", "Loud", "Loving", "Loyal", "Macabre", "Mad",
    "Magical", "Magnificent", "Malevolent", "Medieval", "Memorable", "Mere", "Merry", "Mighty",
    "Mischievous", "Miserable", "Modified", "Moody", "Most", "Mysterious", "Mystical", "Needy",
    "Nervous", "Nice", "Objective", "Obnoxious", "Obsessive", "Obvious", "Opinionated", "Orange",
    "Painful", "Passionate", "Perfect", "Pink", "Playful", "Poisonous", "Polite", "Poor", "Popular", "Powerful",
    "Precise", "Preserved", "Pretty", "Purple", "Quick", "Quiet", "Random", "Rapid", "Rare", "Real",
    "Reassuring", "Reckless", "Red", "Regular", "Remorseful", "Responsible", "Rich", "Rude", "Ruthless",
    "Sad", "Scared", "Scary", "Scornful", "Screaming", "Selfish", "Serious", "Shady", "Shaky", "Sharp",
    "Shiny", "Shy", "Simple", "Sleepy", "Slow", "Sly", "Small", "Smart", "Smelly", "Smiling", "Smooth",
    "Smug", "Sober", "Soft", "Solemn", "Square", "Square", "Steady", "Strange", "Strong",
    "Stunning", "Subjective", "Successful", "Surly", "Sweet", "Tactful", "Tense",
    "Thoughtful", "Tight", "Tiny", "Tolerant", "Uneasy", "Unique", "Unseen", "Warm", "Weak",
    "Weird", "WellCooked", "Wild", "Wise", "Witty", "Wonderful", "Worried", "Yellow", "Young",
    "Zealous"
    ];
//var pronouns = [
//];
//var conjunctions = [
//"And", "Or", "For", "Above", "Before", "Against", "Between"
//];

/*
 * Maps a string (category name) to the array of words from that category.
 */
var CATEGORIES =
{
    //"_NOUN_": nouns,
    "_PLURALNOUN_": pluralNouns,
    //"_PLACE_": places,
    "_VERB_": verbs,
    "_ADVERB_": adverbs,
    "_ADJECTIVE_": adjectives
    //"_PRONOUN_": pronouns,
    //"_CONJUNCTION_": conjunctions,
};

var PATTERNS = [
    "_ADJECTIVE__PLURALNOUN__VERB__ADVERB_"

    // BeautifulFungiOrSpaghetti
    //"_ADJECTIVE__PLURALNOUN__CONJUNCTION__PLURALNOUN_",

    // AmazinglyScaryToy
    //"_ADVERB__ADJECTIVE__NOUN_",

    // NeitherTrashNorRifle
    //"Neither_NOUN_Nor_NOUN_",
    //"Either_NOUN_Or_NOUN_",

    // EitherCopulateOrInvestigate
    //"Either_VERB_Or_VERB_",
    //"Neither_VERB_Nor_VERB_",

    //"The_ADJECTIVE__ADJECTIVE__NOUN_",
    //"The_ADVERB__ADJECTIVE__NOUN_",
    //"The_ADVERB__ADJECTIVE__NOUN_s",
    //"The_ADVERB__ADJECTIVE__PLURALNOUN__VERB_",

    // WolvesComputeBadly
    //"_PLURALNOUN__VERB__ADVERB_",

    // UniteFacilitateAndMerge
    //"_VERB__VERB_And_VERB_",

    //NastyWitchesAtThePub
    //"_ADJECTIVE__PLURALNOUN_AtThe_PLACE_",
];

/*
 * Returns a random element from the array 'arr'
 */
function randomElement(arr) {
    return arr[Math.floor(Math.random() * arr.length)];
}

/*
 * Returns true if the string 's' contains one of the
 * template strings.
 */
function hasTemplate(s) {
    for (var template in CATEGORIES){
        if (s.indexOf(template) >= 0){
            return true;
        }
    }
}

/**
 * Generates new room name.
 */
var RoomNameGenerator = {
    generateRoomWithoutSeparator: function() {
        // Note that if more than one pattern is available, the choice of
        // 'name' won't have a uniform distribution amongst all patterns (names
        // from patterns with fewer options will have higher probability of
        // being chosen that names from patterns with more options).
        var name = randomElement(PATTERNS);
        var word;
        while (hasTemplate(name)) {
            for (var template in CATEGORIES) {
                word = randomElement(CATEGORIES[template]);
                name = name.replace(template, word);
            }
        }

        return name;
    }
};

module.exports = RoomNameGenerator;
<commit_msg>Removes words from the roomname generator.<commit_after>/* jshint -W101 */
//var nouns = [
//];
var pluralNouns = [
    "Aliens", "Animals", "Antelopes", "Ants", "Apes", "Apples", "Baboons", "Bacteria", "Badgers", "Bananas", "Bats",
    "Bears", "Birds", "Bonobos", "Brides", "Bugs", "Bulls", "Butterflies", "Cheetahs",
    "Cherries", "Chicken", "Children", "Chimps", "Clowns", "Cows", "Creatures", "Dinosaurs", "Dogs", "Dolphins",
    "Donkeys", "Dragons", "Ducks", "Dwarfs", "Eagles", "Elephants", "Elves", "Fathers",
    "Fish", "Flowers", "Frogs", "Fruit", "Fungi", "Galaxies", "Geese", "Goats",
    "Gorillas", "Hedgehogs", "Hippos", "Horses", "Hunters", "Insects", "Kids", "Knights",
    "Lemons", "Lemurs", "Leopards", "LifeForms", "Lions", "Lizards", "Mice", "Monkeys", "Monsters",
    "Mushrooms", "Octopodes", "Oranges", "Orangutans", "Organisms", "Pants", "Parrots", "Penguins",
    "People", "Pigeons", "Pigs", "Pineapples", "Plants", "Potatoes", "Priests", "Rats", "Reptiles", "Reptilians",
    "Rhinos", "Seagulls", "Sheep", "Siblings", "Snakes", "Spaghetti", "Spiders", "Squid", "Squirrels",
    "Stars", "Students", "Teachers", "Tigers", "Tomatoes", "Trees", "Vampires", "Vegetables", "Viruses", "Vulcans",
    "Warewolves", "Weasels", "Whales", "Witches", "Wizards", "Wolves", "Workers", "Worms", "Zebras"
];
//var places = [
//"Pub", "University", "Airport", "Library", "Mall", "Theater", "Stadium", "Office", "Show", "Gallows", "Beach",
// "Cemetery", "Hospital", "Reception", "Restaurant", "Bar", "Church", "House", "School", "Square", "Village",
// "Cinema", "Movies", "Party", "Restroom", "End", "Jail", "PostOffice", "Station", "Circus", "Gates", "Entrance",
// "Bridge"
//];
var verbs = [
    "Abandon", "Adapt", "Advertise", "Answer", "Anticipate", "Appreciate",
    "Approach", "Argue", "Ask", "Bite", "Blossom", "Blush", "Breathe", "Breed", "Bribe", "Burn", "Calculate",
    "Clean", "Code", "Communicate", "Compute", "Confess", "Confiscate", "Conjugate", "Conjure", "Consume",
    "Contemplate", "Crawl", "Dance", "Delegate", "Devour", "Develop", "Differ", "Discuss",
    "Dissolve", "Drink", "Eat", "Elaborate", "Emancipate", "Estimate", "Expire", "Extinguish",
    "Extract", "Facilitate", "Fall", "Feed", "Finish", "Floss", "Fly", "Follow", "Fragment", "Freeze",
    "Gather", "Glow", "Grow", "Hex", "Hide", "Hug", "Hurry", "Improve", "Intersect", "Investigate", "Jinx",
    "Joke", "Jubilate", "Kiss", "Laugh", "Manage", "Meet", "Merge", "Move", "Object", "Observe", "Offer",
    "Paint", "Participate", "Party", "Perform", "Plan", "Pursue", "Pierce", "Play", "Postpone", "Pray", "Proclaim",
    "Question", "Read", "Reckon", "Rejoice", "Represent", "Resize", "Rhyme", "Scream", "Search", "Select", "Share", "Shoot",
    "Shout", "Signal", "Sing", "Skate", "Sleep", "Smile", "Smoke", "Solve", "Spell", "Steer", "Stink",
    "Substitute", "Swim", "Taste", "Teach", "Terminate", "Think", "Type", "Unite", "Vanish", "Worship"
];
var adverbs = [
    "Absently", "Accurately", "Accusingly", "Adorably", "AllTheTime", "Alone", "Always", "Amazingly", "Angrily",
    "Anxiously", "Anywhere", "Appallingly", "Apparently", "Articulately", "Astonishingly", "Badly", "Barely",
    "Beautifully", "Blindly", "Bravely", "Brightly", "Briskly", "Brutally", "Calmly", "Carefully", "Casually",
    "Cautiously", "Cleverly", "Constantly", "Correctly", "Crazily", "Curiously", "Cynically", "Daily",
    "Dangerously", "Deliberately", "Delicately", "Desperately", "Discreetly", "Eagerly", "Easily", "Euphoricly",
    "Evenly", "Everywhere", "Exactly", "Expectantly", "Extensively", "Ferociously", "Fiercely", "Finely",
    "Flatly", "Frequently", "Frighteningly", "Gently", "Gloriously", "Grimly", "Guiltily", "Happily",
    "Hard", "Hastily", "Heroically", "High", "Highly", "Hourly", "Humbly", "Hysterically", "Immensely",
    "Impartially", "Impolitely", "Indifferently", "Intensely", "Jealously", "Jovially", "Kindly", "Lazily",
    "Lightly", "Loudly", "Lovingly", "Loyally", "Magnificently", "Malevolently", "Merrily", "Mightily", "Miserably",
    "Mysteriously", "NOT", "Nervously", "Nicely", "Nowhere", "Objectively", "Obnoxiously", "Obsessively",
    "Obviously", "Often", "Painfully", "Patiently", "Playfully", "Politely", "Poorly", "Precisely", "Promptly",
    "Quickly", "Quietly", "Randomly", "Rapidly", "Rarely", "Recklessly", "Regularly", "Remorsefully", "Responsibly",
    "Rudely", "Ruthlessly", "Sadly", "Scornfully", "Seamlessly", "Seldom", "Selfishly", "Seriously", "Shakily",
    "Sharply", "Sideways", "Silently", "Sleepily", "Slightly", "Slowly", "Slyly", "Smoothly", "Softly", "Solemnly",
    "Steadily", "Sternly", "Strangely", "Strongly", "Stunningly", "Surely", "Tenderly", "Thoughtfully",
    "Tightly", "Uneasily", "Vanishingly", "Violently", "Warmly", "Weakly", "Wearily", "Weekly", "Weirdly", "Well",
    "Well", "Wickedly", "Wildly", "Wisely", "Wonderfully", "Yearly"
];
var adjectives = [
    "Abominable", "Accurate", "Adorable", "All", "Alleged", "Ancient", "Angry", "Angry", "Anxious", "Appalling",
    "Apparent", "Astonishing", "Attractive", "Awesome", "Baby", "Bad", "Beautiful", "Benign", "Big", "Bitter",
    "Blind", "Blue", "Bold", "Brave", "Bright", "Brisk", "Calm", "Camouflaged", "Casual", "Cautious",
    "Choppy", "Chosen", "Clever", "Cold", "Cool", "Crawly", "Crazy", "Creepy", "Cruel", "Curious", "Cynical",
    "Dangerous", "Dark", "Delicate", "Desperate", "Difficult", "Discreet", "Disguised", "Dizzy",
    "Dumb", "Eager", "Easy", "Edgy", "Electric", "Elegant", "Emancipated", "Enormous", "Euphoric", "Evil",
    "Fast", "Ferocious", "Fierce", "Fine", "Flawed", "Flying", "Foolish", "Foxy",
    "Freezing", "Funny", "Furious", "Gentle", "Glorious", "Golden", "Good", "Green", "Green", "Guilty",
    "Hairy", "Happy", "Hard", "Hasty", "Hazy", "Heroic", "Hostile", "Hot", "Humble", "Humongous",
    "Humorous", "Hysterical", "Idealistic", "Ignorant", "Immense", "Impartial", "Impolite", "Indifferent",
    "Infuriated", "Insightful", "Intense", "Interesting", "Intimidated", "Intriguing", "Jealous", "Jolly", "Jovial",
    "Jumpy", "Kind", "Laughing", "Lazy", "Liquid", "Lonely", "Longing", "Loud", "Loving", "Loyal", "Macabre", "Mad",
    "Magical", "Magnificent", "Malevolent", "Medieval", "Memorable", "Mere", "Merry", "Mighty",
    "Mischievous", "Miserable", "Modified", "Moody", "Most", "Mysterious", "Mystical", "Needy",
    "Nervous", "Nice", "Objective", "Obnoxious", "Obsessive", "Obvious", "Opinionated", "Orange",
    "Painful", "Passionate", "Perfect", "Pink", "Playful", "Poisonous", "Polite", "Poor", "Popular", "Powerful",
    "Precise", "Preserved", "Pretty", "Purple", "Quick", "Quiet", "Random", "Rapid", "Rare", "Real",
    "Reassuring", "Reckless", "Red", "Regular", "Remorseful", "Responsible", "Rich", "Rude", "Ruthless",
    "Sad", "Scared", "Scary", "Scornful", "Screaming", "Selfish", "Serious", "Shady", "Shaky", "Sharp",
    "Shiny", "Shy", "Simple", "Sleepy", "Slow", "Sly", "Small", "Smart", "Smelly", "Smiling", "Smooth",
    "Smug", "Sober", "Soft", "Solemn", "Square", "Square", "Steady", "Strange", "Strong",
    "Stunning", "Subjective", "Successful", "Surly", "Sweet", "Tactful", "Tense",
    "Thoughtful", "Tight", "Tiny", "Tolerant", "Uneasy", "Unique", "Unseen", "Warm", "Weak",
    "Weird", "WellCooked", "Wild", "Wise", "Witty", "Wonderful", "Worried", "Yellow", "Young",
    "Zealous"
    ];
//var pronouns = [
//];
//var conjunctions = [
//"And", "Or", "For", "Above", "Before", "Against", "Between"
//];

/*
 * Maps a string (category name) to the array of words from that category.
 */
var CATEGORIES =
{
    //"_NOUN_": nouns,
    "_PLURALNOUN_": pluralNouns,
    //"_PLACE_": places,
    "_VERB_": verbs,
    "_ADVERB_": adverbs,
    "_ADJECTIVE_": adjectives
    //"_PRONOUN_": pronouns,
    //"_CONJUNCTION_": conjunctions,
};

var PATTERNS = [
    "_ADJECTIVE__PLURALNOUN__VERB__ADVERB_"

    // BeautifulFungiOrSpaghetti
    //"_ADJECTIVE__PLURALNOUN__CONJUNCTION__PLURALNOUN_",

    // AmazinglyScaryToy
    //"_ADVERB__ADJECTIVE__NOUN_",

    // NeitherTrashNorRifle
    //"Neither_NOUN_Nor_NOUN_",
    //"Either_NOUN_Or_NOUN_",

    // EitherCopulateOrInvestigate
    //"Either_VERB_Or_VERB_",
    //"Neither_VERB_Nor_VERB_",

    //"The_ADJECTIVE__ADJECTIVE__NOUN_",
    //"The_ADVERB__ADJECTIVE__NOUN_",
    //"The_ADVERB__ADJECTIVE__NOUN_s",
    //"The_ADVERB__ADJECTIVE__PLURALNOUN__VERB_",

    // WolvesComputeBadly
    //"_PLURALNOUN__VERB__ADVERB_",

    // UniteFacilitateAndMerge
    //"_VERB__VERB_And_VERB_",

    //NastyWitchesAtThePub
    //"_ADJECTIVE__PLURALNOUN_AtThe_PLACE_",
];

/*
 * Returns a random element from the array 'arr'
 */
function randomElement(arr) {
    return arr[Math.floor(Math.random() * arr.length)];
}

/*
 * Returns true if the string 's' contains one of the
 * template strings.
 */
function hasTemplate(s) {
    for (var template in CATEGORIES){
        if (s.indexOf(template) >= 0){
            return true;
        }
    }
}

/**
 * Generates new room name.
 */
var RoomNameGenerator = {
    generateRoomWithoutSeparator: function() {
        // Note that if more than one pattern is available, the choice of
        // 'name' won't have a uniform distribution amongst all patterns (names
        // from patterns with fewer options will have higher probability of
        // being chosen that names from patterns with more options).
        var name = randomElement(PATTERNS);
        var word;
        while (hasTemplate(name)) {
            for (var template in CATEGORIES) {
                word = randomElement(CATEGORIES[template]);
                name = name.replace(template, word);
            }
        }

        return name;
    }
};

module.exports = RoomNameGenerator;
<|endoftext|>


<commit_before>const { Service, checkGitStatus, executeScriptAsync } = require('clean-scripts')
const { watch } = require('watch-then-execute')

const tsFiles = `"packages/@(core|vue|react|angular)/@(src|demo)/**/*.@(ts|tsx)" "spec/**/*.ts" "screenshots/**/*.ts"`
const lessFiles = `"packages/core/src/**/*.less" "packages/core/demo/**/*.less"`
const jsFiles = `"*.config.js" "spec/**/*.config.js"`
const excludeTsFiles = `"packages/@(core|vue|react|angular)/@(src|demo)/**/*.d.ts"`

const vueTemplateCommand = `file2variable-cli --config packages/vue/src/file2variable.config.js`
const tscSrcCommand = [
  `tsc -p packages/core/src`,
  `tsc -p packages/vue/src`,
  `tsc -p packages/react/src`
]
const tscDemoCommand = [
  `tsc -p packages/core/demo`,
  `tsc -p packages/vue/demo`,
  `tsc -p packages/react/demo`
]
const webpackCommand = `webpack`
const revStaticCommand = `rev-static`
const cssCommand = [
  `lessc packages/core/src/grid.less -sm=on > packages/core/src/grid.css`,
  `postcss packages/core/src/grid.css -o packages/core/dist/grid.css`,
  `cleancss packages/core/dist/grid.css ./packages/core/node_modules/perfect-scrollbar/css/perfect-scrollbar.css -o packages/core/dist/grid.min.css`,
  `lessc packages/core/demo/index.less -sm=on > packages/core/demo/index.css`,
  `cleancss packages/core/dist/grid.min.css packages/core/demo/index.css ./node_modules/github-fork-ribbon-css/gh-fork-ribbon.css -o packages/core/demo/index.bundle.css`
]

module.exports = {
  build: [
    {
      js: [
        vueTemplateCommand,
        tscSrcCommand,
        tscDemoCommand,
        webpackCommand
      ],
      css: cssCommand,
      clean: `rimraf "packages/@(core|vue|react|angular)/demo/**/@(*.bundle-*.js|*.bundle-*.css)"`
    },
    revStaticCommand
  ],
  lint: {
    ts: `tslint ${tsFiles} --exclude ${excludeTsFiles}`,
    js: `standard ${jsFiles}`,
    less: `stylelint ${lessFiles}`,
    export: `no-unused-export ${tsFiles} ${lessFiles} --exclude ${excludeTsFiles}`,
    commit: `commitlint --from=HEAD~1`,
    markdown: `markdownlint README.md`
  },
  test: [
    'tsc -p spec',
    'karma start spec/karma.config.js',
    () => checkGitStatus()
  ],
  fix: {
    ts: `tslint --fix ${tsFiles}`,
    js: `standard --fix ${jsFiles}`,
    less: `stylelint --fix ${lessFiles}`
  },
  watch: {
    template: `${vueTemplateCommand} --watch`,
    src: `${tscSrcCommand} --watch`,
    demo: `${tscDemoCommand} --watch`,
    webpack: `${webpackCommand} --watch`,
    less: () => watch(['packages/core/src/*.less', 'packages/core/demo/*.less'], [], () => executeScriptAsync(cssCommand)),
    rev: `${revStaticCommand} --watch`
  },
  screenshot: [
    new Service(`http-server -p 8000`),
    `tsc -p screenshots`,
    `node screenshots/index.js`
  ]
}
<commit_msg>build: improve scripts<commit_after>const { Service, checkGitStatus, executeScriptAsync } = require('clean-scripts')
const { watch } = require('watch-then-execute')

const tsFiles = `"packages/@(core|vue|react|angular)/@(src|demo)/**/*.@(ts|tsx)" "spec/**/*.ts" "screenshots/**/*.ts"`
const lessFiles = `"packages/core/src/**/*.less" "packages/core/demo/**/*.less"`
const jsFiles = `"*.config.js" "spec/**/*.config.js"`
const excludeTsFiles = `"packages/@(core|vue|react|angular)/@(src|demo)/**/*.d.ts"`

const vueTemplateCommand = `file2variable-cli --config packages/vue/src/file2variable.config.js`
const tscCoreSrcCommand = `tsc -p packages/core/src`
const tscVueSrcCommand = `tsc -p packages/vue/src`
const tscReactSrcCommand = `tsc -p packages/react/src`

const tscCoreDemoCommand = `tsc -p packages/core/demo`
const tscVueDemoCommand = `tsc -p packages/vue/demo`
const tscReactDemoCommand = `tsc -p packages/react/demo`

const webpackCommand = `webpack`
const revStaticCommand = `rev-static`
const cssCommand = [
  `lessc packages/core/src/grid.less -sm=on > packages/core/src/grid.css`,
  `postcss packages/core/src/grid.css -o packages/core/dist/grid.css`,
  `cleancss packages/core/dist/grid.css ./packages/core/node_modules/perfect-scrollbar/css/perfect-scrollbar.css -o packages/core/dist/grid.min.css`,
  `lessc packages/core/demo/index.less -sm=on > packages/core/demo/index.css`,
  `cleancss packages/core/dist/grid.min.css packages/core/demo/index.css ./node_modules/github-fork-ribbon-css/gh-fork-ribbon.css -o packages/core/demo/index.bundle.css`
]

module.exports = {
  build: [
    {
      js: [
        vueTemplateCommand,
        tscCoreSrcCommand,
        {
          tscVueSrcCommand,
          tscReactSrcCommand
        },
        tscCoreDemoCommand,
        {
          tscVueDemoCommand,
          tscReactDemoCommand
        },
        webpackCommand
      ],
      css: cssCommand,
      clean: `rimraf "packages/@(core|vue|react|angular)/demo/**/@(*.bundle-*.js|*.bundle-*.css)"`
    },
    revStaticCommand
  ],
  lint: {
    ts: `tslint ${tsFiles} --exclude ${excludeTsFiles}`,
    js: `standard ${jsFiles}`,
    less: `stylelint ${lessFiles}`,
    export: `no-unused-export ${tsFiles} ${lessFiles} --exclude ${excludeTsFiles}`,
    commit: `commitlint --from=HEAD~1`,
    markdown: `markdownlint README.md`
  },
  test: [
    'tsc -p spec',
    'karma start spec/karma.config.js',
    () => checkGitStatus()
  ],
  fix: {
    ts: `tslint --fix ${tsFiles}`,
    js: `standard --fix ${jsFiles}`,
    less: `stylelint --fix ${lessFiles}`
  },
  watch: {
    vueTemplateCommand: `${vueTemplateCommand} --watch`,
    tscCoreSrcCommand: `${tscCoreSrcCommand} --watch`,
    tscVueSrcCommand: `${tscVueSrcCommand} --watch`,
    tscReactSrcCommand: `${tscReactSrcCommand} --watch`,
    tscCoreDemoCommand: `${tscCoreDemoCommand} --watch`,
    tscVueDemoCommand: `${tscVueDemoCommand} --watch`,
    tscReactDemoCommand: `${tscReactDemoCommand} --watch`,
    webpack: `${webpackCommand} --watch`,
    less: () => watch(['packages/core/src/*.less', 'packages/core/demo/*.less'], [], () => executeScriptAsync(cssCommand)),
    rev: `${revStaticCommand} --watch`
  },
  screenshot: [
    new Service(`http-server -p 8000`),
    `tsc -p screenshots`,
    `node screenshots/index.js`
  ]
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @emails react-core
 */

'use strict';

var React;
var ReactNoop;

describe('ReactIncrementalTriangle', () => {
  beforeEach(() => {
    jest.resetModules();
    React = require('react');
    ReactNoop = require('react-noop-renderer');
  });

  function span(prop) {
    return {type: 'span', children: [], prop};
  }

  const FLUSH = 'FLUSH';
  function flush(unitsOfWork = Infinity) {
    return {
      type: FLUSH,
      unitsOfWork,
    };
  }

  const STEP = 'STEP';
  function step(counter) {
    return {
      type: STEP,
      counter,
    };
  }

  const INTERRUPT = 'INTERRUPT';
  function interrupt(key) {
    return {
      type: INTERRUPT,
    };
  }

  const TOGGLE = 'TOGGLE';
  function toggle(childIndex) {
    return {
      type: TOGGLE,
      childIndex,
    };
  }

  const EXPIRE = 'EXPIRE';
  function expire(ms) {
    return {
      type: EXPIRE,
      ms,
    };
  }

  function TriangleSimulator() {
    let triangles = [];
    let leafTriangles = [];
    let yieldAfterEachRender = false;
    class Triangle extends React.Component {
      constructor(props) {
        super();
        this.index = triangles.length;
        triangles.push(this);
        if (props.depth === 0) {
          this.leafIndex = leafTriangles.length;
          leafTriangles.push(this);
        }
        this.state = {isActive: false};
      }
      activate() {
        if (this.props.depth !== 0) {
          throw new Error('Cannot activate non-leaf component');
        }
        this.setState({isActive: true});
      }
      deactivate() {
        if (this.props.depth !== 0) {
          throw new Error('Cannot deactivate non-leaf component');
        }
        this.setState({isActive: false});
      }
      shouldComponentUpdate(nextProps, nextState) {
        return (
          this.props.counter !== nextProps.counter ||
          this.state.isActive !== nextState.isActive
        );
      }
      render() {
        if (yieldAfterEachRender) {
          ReactNoop.yield(this);
        }
        const {counter, depth} = this.props;
        if (depth === 0) {
          if (this.state.isActive) {
            return ;
          }
          return ;
        }
        return [
          <Triangle key={1} counter={counter} depth={depth - 1} />,
          <Triangle key={2} counter={counter} depth={depth - 1} />,
          <Triangle key={3} counter={counter} depth={depth - 1} />,
        ];
      }
    }

    let appInstance;
    class App extends React.Component {
      state = {counter: 0};
      interrupt() {
        // Triggers a restart from the top.
        this.forceUpdate();
      }
      setCounter(counter) {
        const currentCounter = this.state.counter;
        this.setState({counter});
        return currentCounter;
      }
      render() {
        appInstance = this;
        return <Triangle counter={this.state.counter} depth={3} />;
      }
    }

    const depth = 3;

    let keyCounter = 0;
    function reset(nextStep = 0) {
      triangles = [];
      leafTriangles = [];
      // Remounts the whole tree by changing the key
      ReactNoop.render(<App depth={depth} key={keyCounter++} />);
      ReactNoop.flush();
      assertConsistentTree();
      return appInstance;
    }

    reset();
    const totalChildren = leafTriangles.length;
    const totalTriangles = triangles.length;

    function assertConsistentTree(activeTriangle, counter) {
      const activeIndex = activeTriangle ? activeTriangle.leafIndex : -1;

      const children = ReactNoop.getChildren();
      for (let i = 0; i < children.length; i++) {
        let child = children[i];
        let num = child.prop;

        // If an expected counter is not specified, use the value of the
        // first child.
        if (counter === undefined) {
          if (typeof num === 'string') {
            counter = parseInt(num.substr(1, num.length - 2), 10);
          } else {
            counter = num;
          }
        }

        if (i === activeIndex) {
          if (num !== `*${counter}*`) {
            throw new Error(
              `Triangle ${i} is inconsistent: ${num} instead of *${counter}*.`,
            );
          }
        } else {
          if (num !== counter) {
            throw new Error(
              `Triangle ${i} is inconsistent: ${num} instead of ${counter}.`,
            );
          }
        }
      }
    }

    function simulate(...actions) {
      const app = reset();
      let expectedCounterAtEnd = app.state.counter;

      let activeTriangle = null;
      for (var i = 0; i < actions.length; i++) {
        ReactNoop.flushSync(() => {
          const action = actions[i];
          switch (action.type) {
            case FLUSH:
              ReactNoop.flushUnitsOfWork(action.unitsOfWork);
              break;
            case STEP:
              ReactNoop.deferredUpdates(() => {
                app.setCounter(action.counter);
                expectedCounterAtEnd = action.counter;
              });
              break;
            case INTERRUPT:
              app.interrupt();
              break;
            case TOGGLE:
              const targetTriangle = leafTriangles[action.childIndex];
              if (targetTriangle === undefined) {
                throw new Error('Target index is out of bounds');
              }
              if (targetTriangle === activeTriangle) {
                activeTriangle = null;
                targetTriangle.deactivate();
              } else {
                if (activeTriangle !== null) {
                  activeTriangle.deactivate();
                }
                activeTriangle = targetTriangle;
                targetTriangle.activate();
              }
              break;
            case EXPIRE:
              ReactNoop.expire(action.ms);
              break;
            default:
              break;
          }
        });
        assertConsistentTree(activeTriangle);
      }
      // Flush remaining work
      ReactNoop.flush();
      assertConsistentTree(activeTriangle, expectedCounterAtEnd);
    }

    return {simulate, totalChildren, totalTriangles};
  }

  it('renders the triangle demo without inconsistencies', () => {
    const {simulate} = TriangleSimulator();
    simulate(step(1));
    simulate(toggle(0), step(1), toggle(0));
    simulate(step(1), toggle(0), flush(2), step(2), toggle(0));
    simulate(step(1), flush(3), toggle(0), step(0));
    simulate(step(1), flush(3), toggle(18), step(0));
    simulate(step(4), flush(52), expire(1476), flush(17), step(0));
    simulate(interrupt(), toggle(10), step(2), expire(990), flush(46));
    simulate(interrupt(), step(6), step(7), toggle(6), interrupt());
  });

  it('fuzz tester', () => {
    // This test is not deterministic because the inputs are randomized. It runs
    // a limited number of tests on every run. If it fails, it will output the
    // case that led to the failure. Add the failing case to the test above
    // to prevent future regressions.
    const {simulate, totalTriangles, totalChildren} = TriangleSimulator();

    const limit = 1000;

    function randomInteger(min, max) {
      min = Math.ceil(min);
      max = Math.floor(max);
      return Math.floor(Math.random() * (max - min)) + min;
    }

    function randomAction() {
      switch (randomInteger(0, 5)) {
        case 0:
          return flush(randomInteger(0, totalTriangles * 1.5));
        case 1:
          return step(randomInteger(0, 10));
        case 2:
          return interrupt();
        case 3:
          return toggle(randomInteger(0, totalChildren));
        case 4:
          return expire(randomInteger(0, 1500));
        default:
          throw new Error('Switch statement should be exhaustive');
      }
    }

    function randomActions(n) {
      let actions = [];
      for (let i = 0; i < n; i++) {
        actions.push(randomAction());
      }
      return actions;
    }

    function formatActions(actions) {
      let result = 'simulate(';
      for (let i = 0; i < actions.length; i++) {
        const action = actions[i];
        switch (action.type) {
          case FLUSH:
            result += `flush(${action.unitsOfWork})`;
            break;
          case STEP:
            result += `step(${action.counter})`;
            break;
          case INTERRUPT:
            result += 'interrupt()';
            break;
          case TOGGLE:
            result += `toggle(${action.childIndex})`;
            break;
          case EXPIRE:
            result += `expire(${action.ms})`;
            break;
          default:
            throw new Error('Switch statement should be exhaustive');
        }
        if (i !== actions.length - 1) {
          result += ', ';
        }
      }
      result += ')';
      return result;
    }

    for (let i = 0; i < limit; i++) {
      const actions = randomActions(5);
      try {
        simulate(...actions);
      } catch (e) {
        console.error(
          `
Triangle fuzz tester error! Copy and paste the following line into the test suite:
  ${formatActions(actions)}
        `,
        );
        throw e;
      }
    }
  });
});
<commit_msg>Fuzz test across multiple roots (#11376)<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @emails react-core
 */

'use strict';

var React;
var ReactNoop;

describe('ReactIncrementalTriangle', () => {
  beforeEach(() => {
    jest.resetModules();
    React = require('react');
    ReactNoop = require('react-noop-renderer');
  });

  function span(prop) {
    return {type: 'span', children: [], prop};
  }

  const FLUSH = 'FLUSH';
  function flush(unitsOfWork = Infinity) {
    return {
      type: FLUSH,
      unitsOfWork,
    };
  }

  const STEP = 'STEP';
  function step(counter) {
    return {
      type: STEP,
      counter,
    };
  }

  const INTERRUPT = 'INTERRUPT';
  function interrupt(key) {
    return {
      type: INTERRUPT,
    };
  }

  const TOGGLE = 'TOGGLE';
  function toggle(childIndex) {
    return {
      type: TOGGLE,
      childIndex,
    };
  }

  const EXPIRE = 'EXPIRE';
  function expire(ms) {
    return {
      type: EXPIRE,
      ms,
    };
  }

  const STOP = 'STOP';

  function randomInteger(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min)) + min;
  }

  function formatAction(action) {
    switch (action.type) {
      case FLUSH:
        return `flush(${action.unitsOfWork})`;
      case STEP:
        return `step(${action.counter})`;
      case INTERRUPT:
        return 'interrupt()';
      case TOGGLE:
        return `toggle(${action.childIndex})`;
      case EXPIRE:
        return `expire(${action.ms})`;
      default:
        throw new Error('Switch statement should be exhaustive');
    }
  }

  function formatActions(actions) {
    let result = 'simulate(';
    for (let i = 0; i < actions.length; i++) {
      const action = actions[i];
      result += formatAction(action);
      if (i !== actions.length - 1) {
        result += ', ';
      }
    }
    result += ')';
    return result;
  }

  const MAX_DEPTH = 3;
  const TOTAL_CHILDREN = Math.pow(3, MAX_DEPTH);
  let TOTAL_TRIANGLES = 0;
  for (let i = 0; i <= MAX_DEPTH; i++) {
    TOTAL_TRIANGLES += Math.pow(3, i);
  }

  function randomAction() {
    switch (randomInteger(0, 5)) {
      case 0:
        return flush(randomInteger(0, TOTAL_TRIANGLES * 1.5));
      case 1:
        return step(randomInteger(0, 10));
      case 2:
        return interrupt();
      case 3:
        return toggle(randomInteger(0, TOTAL_CHILDREN));
      case 4:
        return expire(randomInteger(0, 1500));
      default:
        throw new Error('Switch statement should be exhaustive');
    }
  }

  function randomActions(n) {
    let actions = [];
    for (let i = 0; i < n; i++) {
      actions.push(randomAction());
    }
    return actions;
  }

  function TriangleSimulator(rootID) {
    let triangles = [];
    let leafTriangles = [];
    let yieldAfterEachRender = false;
    class Triangle extends React.Component {
      constructor(props) {
        super();
        this.index = triangles.length;
        triangles.push(this);
        if (props.depth === 0) {
          this.leafIndex = leafTriangles.length;
          leafTriangles.push(this);
        }
        this.state = {isActive: false};
      }
      activate() {
        if (this.props.depth !== 0) {
          throw new Error('Cannot activate non-leaf component');
        }
        this.setState({isActive: true});
      }
      deactivate() {
        if (this.props.depth !== 0) {
          throw new Error('Cannot deactivate non-leaf component');
        }
        this.setState({isActive: false});
      }
      shouldComponentUpdate(nextProps, nextState) {
        return (
          this.props.counter !== nextProps.counter ||
          this.state.isActive !== nextState.isActive
        );
      }
      render() {
        if (yieldAfterEachRender) {
          ReactNoop.yield(this);
        }
        const {counter, depth} = this.props;
        if (depth === 0) {
          if (this.state.isActive) {
            return ;
          }
          return ;
        }
        return [
          <Triangle key={1} counter={counter} depth={depth - 1} />,
          <Triangle key={2} counter={counter} depth={depth - 1} />,
          <Triangle key={3} counter={counter} depth={depth - 1} />,
        ];
      }
    }

    let appInstance;
    class App extends React.Component {
      state = {counter: 0};
      interrupt() {
        // Triggers a restart from the top.
        this.forceUpdate();
      }
      setCounter(counter) {
        const currentCounter = this.state.counter;
        this.setState({counter});
        return currentCounter;
      }
      render() {
        appInstance = this;
        return <Triangle counter={this.state.counter} depth={3} />;
      }
    }

    let keyCounter = 0;
    function reset(nextStep = 0) {
      triangles = [];
      leafTriangles = [];
      // Remounts the whole tree by changing the key
      if (rootID) {
        ReactNoop.renderToRootWithID(
          <App depth={MAX_DEPTH} key={keyCounter++} />,
          rootID,
        );
      } else {
        ReactNoop.render(<App depth={MAX_DEPTH} key={keyCounter++} />);
      }
      ReactNoop.flush();
      assertConsistentTree();
      return appInstance;
    }

    reset();

    function assertConsistentTree(activeTriangle, counter) {
      const activeIndex = activeTriangle ? activeTriangle.leafIndex : -1;

      const children = ReactNoop.getChildren(rootID);

      if (children.length !== TOTAL_CHILDREN) {
        throw new Error('Wrong number of children.');
      }

      for (let i = 0; i < children.length; i++) {
        let child = children[i];
        let num = child.prop;

        // If an expected counter is not specified, use the value of the
        // first child.
        if (counter === undefined) {
          if (typeof num === 'string') {
            counter = parseInt(num.substr(1, num.length - 2), 10);
          } else {
            counter = num;
          }
        }

        if (i === activeIndex) {
          if (num !== `*${counter}*`) {
            throw new Error(
              `Triangle ${i} is inconsistent: ${num} instead of *${counter}*.`,
            );
          }
        } else {
          if (num !== counter) {
            throw new Error(
              `Triangle ${i} is inconsistent: ${num} instead of ${counter}.`,
            );
          }
        }
      }
    }

    function* simulateAndYield() {
      const app = reset();
      let expectedCounterAtEnd = app.state.counter;

      let activeTriangle = null;
      while (true) {
        var action = yield;
        if (action === STOP) {
          break;
        }
        ReactNoop.flushSync(() => {
          switch (action.type) {
            case FLUSH:
              ReactNoop.flushUnitsOfWork(action.unitsOfWork);
              break;
            case STEP:
              ReactNoop.deferredUpdates(() => {
                app.setCounter(action.counter);
                expectedCounterAtEnd = action.counter;
              });
              break;
            case INTERRUPT:
              app.interrupt();
              break;
            case TOGGLE:
              const targetTriangle = leafTriangles[action.childIndex];
              if (targetTriangle === undefined) {
                throw new Error('Target index is out of bounds');
              }
              if (targetTriangle === activeTriangle) {
                activeTriangle = null;
                targetTriangle.deactivate();
              } else {
                if (activeTriangle !== null) {
                  activeTriangle.deactivate();
                }
                activeTriangle = targetTriangle;
                targetTriangle.activate();
              }
              break;
            case EXPIRE:
              ReactNoop.expire(action.ms);
              break;
            default:
              break;
          }
        });
        assertConsistentTree(activeTriangle);
      }
      // Flush remaining work
      ReactNoop.flush();
      assertConsistentTree(activeTriangle, expectedCounterAtEnd);
    }

    function simulate(...actions) {
      const gen = simulateAndYield();
      for (let action of actions) {
        gen.next(action);
      }
      gen.next(STOP);
    }

    return {
      simulateAndYield,
      simulate,
      randomAction,
      randomActions,
    };
  }

  describe('single root', () => {
    // These tests are not deterministic because the inputs are randomized. It
    // runs a limited number of tests on every run. If it fails, it will output
    // the case that led to the failure. Add the failing case to the test above
    // to prevent future regressions.
    it('hard-coded tests', () => {
      const {simulate} = TriangleSimulator();
      simulate(step(1));
      simulate(toggle(0), step(1), toggle(0));
      simulate(step(1), toggle(0), flush(2), step(2), toggle(0));
      simulate(step(1), flush(3), toggle(0), step(0));
      simulate(step(1), flush(3), toggle(18), step(0));
      simulate(step(4), flush(52), expire(1476), flush(17), step(0));
      simulate(interrupt(), toggle(10), step(2), expire(990), flush(46));
      simulate(interrupt(), step(6), step(7), toggle(6), interrupt());
    });

    it('generative tests', () => {
      const {simulate} = TriangleSimulator();

      const limit = 1000;

      for (let i = 0; i < limit; i++) {
        const actions = randomActions(5);
        try {
          simulate(...actions);
        } catch (e) {
          console.error(
            `Triangle fuzz tester error! Copy and paste the following line into the test suite:
${formatActions(actions)}
          `,
          );
          throw e;
        }
      }
    });
  });

  describe('multiple roots', () => {
    const rootIDs = ['a', 'b', 'c'];

    function randomActionsPerRoot() {
      function randomRootID() {
        const index = randomInteger(0, rootIDs.length);
        return rootIDs[index];
      }

      const actions = [];
      for (let i = 0; i < 10; i++) {
        const rootID = randomRootID();
        const action = randomAction();
        actions.push([rootID, action]);
      }
      return actions;
    }

    function formatActionsPerRoot(actions) {
      let result = 'simulateMultipleRoots(';
      for (let i = 0; i < actions.length; i++) {
        const [rootID, action] = actions[i];
        result += `['${rootID}', ${formatAction(action)}]`;
        if (i !== actions.length - 1) {
          result += ', ';
        }
      }
      result += ')';
      return result;
    }

    function simulateMultipleRoots(...actions) {
      const roots = new Map();
      for (let rootID of rootIDs) {
        const simulator = TriangleSimulator(rootID);
        const generator = simulator.simulateAndYield();
        // Call this once to prepare the generator
        generator.next();
        roots.set(rootID, generator);
      }

      actions.forEach(([rootID, action]) => {
        const generator = roots.get(rootID);
        generator.next(action);
      });
      roots.forEach(generator => {
        generator.next(STOP);
      });
    }

    it('hard-coded tests', () => {
      simulateMultipleRoots(
        ['b', interrupt()],
        ['a', toggle(22)],
        ['c', step(4)],
        ['a', expire(10)],
        ['a', interrupt()],
        ['c', step(2)],
        ['b', interrupt()],
      );
    });

    it('generative tests', () => {
      const limit = 100;
      for (let i = 0; i < limit; i++) {
        const actions = randomActionsPerRoot();
        try {
          simulateMultipleRoots(...actions);
        } catch (e) {
          console.error(
            `Triangle fuzz tester error! Copy and paste the following line into the test suite:
${formatActionsPerRoot(actions)}
              `,
          );
          throw e;
        }
      }
    });
  });
});
<|endoftext|>


<commit_before>"use strict";
import Emitter from "yasmf-emitter";
import GCS from "../../lib/grandCentralStation";

// private property symbols
let _fontFamily = Symbol("fontFamily"), // font family
    _fontSize = Symbol("fontSize"),   // 0 = use system default, otherwise specific point sizes
    _theme = Symbol("theme"),      // light, dark, lightHighContrast, etc.
    _externalResources = Symbol("externalResources"), // link templates to external resources, like Wikipedia
    _showImages = Symbol("images"), // determines if images are shown (if possible)
    _lastDictionary = Symbol("lastDictionary"), // records the last dictionary
    _pageSize = Symbol("pageSize");   // page length (search results, etc.)

export default class Settings extends Emitter {
    constructor() {
        super();
        this[_fontFamily] = undefined; // undefined will use app default
        this[_fontSize] = 0;           // 0 will use app default (from system)
        this[_theme] = "light";
        this[_showImages] = true;
        this[_pageSize] = 100;
        this[_lastDictionary] = undefined; // if undefined, the app will pick the first one
        this[_externalResources] = {
            "Wikipedia": "http://www.wikipedia.org/search-redirect.php?language=en&search=%WORD%",
            "WordNet": "http://wordnetweb.princeton.edu/perl/webwn?s=%WORD%"
        };
    }

    init() {
        super.init();
        this.retrieveSettings();
    }

    _emitChangeNotice(notice, newV, oldV) {
        this.emit(notice + "Changed", newV, oldV);
        this.emit("settingsChanged");
    }

    get entries() {
        return [
            {name: "setting:font-family", key: "fontFamily", value: this.fontFamily, type: "select", options: [
                {name: "setting:font-family:Default", value: undefined},
                {name: "setting:font-family:Helvetica-Neue", value: "HelveticaNeue, 'Helvetica Neue', Helvetica, Arial, sans-serif"},
                {name: "setting:font-family:Lucida-Grande", value: "'Lucida Sans Unicode', 'Lucida Grande', sans-serif"},
                {name: "setting:font-family:Georgia", value: "Georgia, serif"},
                {name: "setting:font-family:Palatino", value: "'Palatino Linotype', 'Book Antiqua', Palatino, serif"},
                {name: "setting:font-family:Times-New-Roman", value: "'Times New Roman', Times, serif"}
            ]},
            {name: "setting:font-size",   key: "fontSize", value: this.fontSize, type: "select", options: [
                {name: "setting:font-size:Default", value: 0},
                {name: "setting:font-size:Tiny", value: 50},
                {name: "setting:font-size:Small", value: 75},
                {name: "setting:font-size:Normal", value: 100},
                {name: "setting:font-size:Large", value: 125},
                {name: "setting:font-size:Huge", value: 150},
                {name: "setting:font-size:Gigantic", value: 250}
            ]},
            {name: "setting:theme", key: "theme", value: this.theme, type: "select", options: [
                {name: "setting:theme:Default", value: "default"},
                {name: "setting:theme:Light", value: "light"},
                {name: "setting:theme:Dark", value: "dark"}
            ]},
            {name: "setting:page-size", key: "pageSize", value: this.pageSize, type: "select", options: [
                {name: "setting:page-size:20", value: 25},
                {name: "setting:page-size:50", value: 50},
                {name: "setting:page-size:80", value: 80},
                {name: "setting:page-size:100", value: 100},
                {name: "setting:page-size:150", value: 150},
                {name: "setting:page-size:200", value: 200}
            ]}
        ]
    }

    retrieveSettings() {
        if (localStorage.fontFamily !== undefined) { this[_fontFamily] = localStorage.fontFamily; }
        if (localStorage.fontSize !== undefined) { this[_fontSize] = localStorage.fontSize; }
        if (localStorage.theme !== undefined) { this[_theme] = localStorage.theme; }
        if (localStorage.showImages !== undefined) { this[_showImages] = localStorage.showImages; }
        if (localStorage.externalResources !== undefined) { this[_externalResources] = JSON.parse(localStorage.externalResources); }
        if (localStorage.pageSize !== undefined) { this[_pageSize] = localStorage.pageSize; }
        if (localStorage.lastDictionary !== undefined) { this[_lastDictionary] = localStorage.lastDictionary; }
        this.emit("settingsRetrieved");
        GCS.emit("APP:SETTINGS:loaded");
        this.emit("settingsChanged"); // this will save the settings again, but that's OK
    }

    persistSettings() {
        if (this.fontFamily !== undefined) { localStorage.fontFamily = this.fontFamily; }
        if (this.fontSize !== undefined) { localStorage.fontSize = this.fontSize; }
        if (this.theme !== undefined) { localStorage.theme = this.theme; }
        if (this.showImages !== undefined) { localStorage.showImages = this.showImages; }
        localStorage.externalResources = JSON.stringify(this.externalResources);
        if (this.pageSize !== undefined) { localStorage.pageSize = this.pageSize; }
        if (this.lastDictionary !== undefined) { localStorage.lastDictionary = this.lastDictionary; }
        this.emit("settingsPersisted");
        GCS.emit("APP:SETTINGS:persisted");
    }

    onSettingsChanged() {
        GCS.emit("APP:SETTINGS:changed");
        // save the settings whenever they change
        this.persistSettings();
    }

    get fontFamily() { return this[_fontFamily]; }
    set fontFamily(v) {
        this._emitChangeNotice("fontFamily", v, this[_fontFamily]);
        this[_fontFamily] = v;
    }

    get fontSize() { return this[_fontSize]; }
    set fontSize(v) {
        this._emitChangeNotice("fontSize", v, this[_fontFamily]);
        this[_fontSize] = v;
    }

    get theme() { return this[_theme]; }
    set theme(v) {
        this._emitChangeNotice("theme", v, this[_theme]);
        this[_theme] = v;
    }

    get showImages() { return this[_showImages]; }
    set showImages(v) {
        this._emitChangeNotice("showImages", v, this[_showImages]);
        this[_showImages] = v;
    }

    get pageSize() { return this[_pageSize]; }
    set pageSize(s) {
        this._emitChangeNotice("pageSize", s, this[_pageSize]);
        this[_pageSize] = s;
    }

    get lastDictionary() { return this[_lastDictionary]; }
    set lastDictionary(d) {
        this._emitChangeNotice("lastDictionary", d, this[_lastDictionary]);
        this[_lastDictionary] = d;
    }

    get externalResources() { return this[_externalResources]; }

    addExternalResource({name, template}) {
        this._emitChangeNotice("externalResources");
        this[_externalResources][name] = template;
    }
    removeExternalResource(name) {
        this[_externalResources][name] = undefined;
    }
}

export let settings = new Settings();
settings.retrieveSettings();

export function createSettings(...args) {
    return new Settings(...args);
}

<commit_msg>We need to be able to test this using a proper localStorage<commit_after>"use strict";
import Emitter from "yasmf-emitter";
import GCS from "../../lib/grandCentralStation";

// private property symbols
let _fontFamily = Symbol("fontFamily"), // font family
    _fontSize = Symbol("fontSize"),   // 0 = use system default, otherwise specific point sizes
    _theme = Symbol("theme"),      // light, dark, lightHighContrast, etc.
    _externalResources = Symbol("externalResources"), // link templates to external resources, like Wikipedia
    _showImages = Symbol("images"), // determines if images are shown (if possible)
    _lastDictionary = Symbol("lastDictionary"), // records the last dictionary
    _pageSize = Symbol("pageSize"),   // page length (search results, etc.)
    _localStorage = Symbol("_localStorage"); // for testing

export default class Settings extends Emitter {
    constructor({localStorage} = {}) {
        super();
        this[_fontFamily] = undefined; // undefined will use app default
        this[_fontSize] = 0;           // 0 will use app default (from system)
        this[_theme] = "light";
        this[_showImages] = true;
        this[_pageSize] = 100;
        this[_lastDictionary] = undefined; // if undefined, the app will pick the first one
        this[_externalResources] = {
            "Wikipedia": "http://www.wikipedia.org/search-redirect.php?language=en&search=%WORD%",
            "WordNet": "http://wordnetweb.princeton.edu/perl/webwn?s=%WORD%"
        };
    }

    init() {
        super.init();
        this.retrieveSettings();
    }

    _emitChangeNotice(notice, newV, oldV) {
        this.emit(notice + "Changed", newV, oldV);
        this.emit("settingsChanged");
    }

    get localStorage() {
        return this[_localStorage] ? this[_localStorage] : (typeof localStorage !== "undefined" ? localStorage : {});
    }

    get entries() {
        return [
            {name: "setting:font-family", key: "fontFamily", value: this.fontFamily, type: "select", options: [
                {name: "setting:font-family:Default", value: undefined},
                {name: "setting:font-family:Helvetica-Neue", value: "HelveticaNeue, 'Helvetica Neue', Helvetica, Arial, sans-serif"},
                {name: "setting:font-family:Lucida-Grande", value: "'Lucida Sans Unicode', 'Lucida Grande', sans-serif"},
                {name: "setting:font-family:Georgia", value: "Georgia, serif"},
                {name: "setting:font-family:Palatino", value: "'Palatino Linotype', 'Book Antiqua', Palatino, serif"},
                {name: "setting:font-family:Times-New-Roman", value: "'Times New Roman', Times, serif"}
            ]},
            {name: "setting:font-size",   key: "fontSize", value: this.fontSize, type: "select", options: [
                {name: "setting:font-size:Default", value: 0},
                {name: "setting:font-size:Tiny", value: 50},
                {name: "setting:font-size:Small", value: 75},
                {name: "setting:font-size:Normal", value: 100},
                {name: "setting:font-size:Large", value: 125},
                {name: "setting:font-size:Huge", value: 150},
                {name: "setting:font-size:Gigantic", value: 250}
            ]},
            {name: "setting:theme", key: "theme", value: this.theme, type: "select", options: [
                {name: "setting:theme:Default", value: "default"},
                {name: "setting:theme:Light", value: "light"},
                {name: "setting:theme:Dark", value: "dark"}
            ]},
            {name: "setting:page-size", key: "pageSize", value: this.pageSize, type: "select", options: [
                {name: "setting:page-size:20", value: 25},
                {name: "setting:page-size:50", value: 50},
                {name: "setting:page-size:80", value: 80},
                {name: "setting:page-size:100", value: 100},
                {name: "setting:page-size:150", value: 150},
                {name: "setting:page-size:200", value: 200}
            ]}
        ]
    }

    retrieveSettings() {
        const storage = this.localStorage;
        if (storage.fontFamily !== undefined) { this[_fontFamily] = storage.fontFamily; }
        if (storage.fontSize !== undefined) { this[_fontSize] = storage.fontSize; }
        if (storage.theme !== undefined) { this[_theme] = storage.theme; }
        if (storage.showImages !== undefined) { this[_showImages] = storage.showImages; }
        if (storage.externalResources !== undefined) { this[_externalResources] = JSON.parse(storage.externalResources); }
        if (storage.pageSize !== undefined) { this[_pageSize] = storage.pageSize; }
        if (storage.lastDictionary !== undefined) { this[_lastDictionary] = storage.lastDictionary; }
        this.emit("settingsRetrieved");
        GCS.emit("APP:SETTINGS:loaded");
        this.emit("settingsChanged"); // this will save the settings again, but that's OK
    }

    persistSettings() {
        const storage = this.localStorage;
        if (this.fontFamily !== undefined) { storage.fontFamily = this.fontFamily; }
        if (this.fontSize !== undefined) { storage.fontSize = this.fontSize; }
        if (this.theme !== undefined) { storage.theme = this.theme; }
        if (this.showImages !== undefined) { storage.showImages = this.showImages; }
        storage.externalResources = JSON.stringify(this.externalResources);
        if (this.pageSize !== undefined) { storage.pageSize = this.pageSize; }
        if (this.lastDictionary !== undefined) { storage.lastDictionary = this.lastDictionary; }
        this.emit("settingsPersisted");
        GCS.emit("APP:SETTINGS:persisted");
    }

    onSettingsChanged() {
        GCS.emit("APP:SETTINGS:changed");
        // save the settings whenever they change
        this.persistSettings();
    }

    get fontFamily() { return this[_fontFamily]; }
    set fontFamily(v) {
        this._emitChangeNotice("fontFamily", v, this[_fontFamily]);
        this[_fontFamily] = v;
    }

    get fontSize() { return this[_fontSize]; }
    set fontSize(v) {
        this._emitChangeNotice("fontSize", v, this[_fontFamily]);
        this[_fontSize] = v;
    }

    get theme() { return this[_theme]; }
    set theme(v) {
        this._emitChangeNotice("theme", v, this[_theme]);
        this[_theme] = v;
    }

    get showImages() { return this[_showImages]; }
    set showImages(v) {
        this._emitChangeNotice("showImages", v, this[_showImages]);
        this[_showImages] = v;
    }

    get pageSize() { return this[_pageSize]; }
    set pageSize(s) {
        this._emitChangeNotice("pageSize", s, this[_pageSize]);
        this[_pageSize] = s;
    }

    get lastDictionary() { return this[_lastDictionary]; }
    set lastDictionary(d) {
        this._emitChangeNotice("lastDictionary", d, this[_lastDictionary]);
        this[_lastDictionary] = d;
    }

    get externalResources() { return this[_externalResources]; }

    addExternalResource({name, template}) {
        this._emitChangeNotice("externalResources");
        this[_externalResources][name] = template;
    }
    removeExternalResource(name) {
        this[_externalResources][name] = undefined;
    }
}

export let settings = new Settings();
settings.retrieveSettings();

export function createSettings(...args) {
    return new Settings(...args);
}

<|endoftext|>


<commit_before>// module Node.Express.Request

exports._getRouteParam = function (req, name, nothing, just) {
    return function () {
        if (req.params[name] != null) {
            return just(req.params[name]);
        }
        return nothing;
    };
};

exports._getRoute = function (req) {
    return function () {
        return req.route.path;
    };
};

exports._getBody = function (req) {
    return function () {
        return req.body;
    };
};

exports._getBodyParam = function (req, name, nothing, just) {
    return function () {
        if (req.body && req.body[name] != null) {
            return just(req.body[name]);
        }
        return nothing;
    };
};

exports._getQueryParam = function (req, name, nothing, just) {
    return function () {
        if (req.query && name in req.query && req.query[name] != null) {
            return just(req.query[name]);
        }
        return nothing;
    };
};

exports._getCookie = function (req, name, nothing, just) {
    return function () {
        if (req.cookies[name] != null) {
            return just(req.cookies[name]);
        }
        return nothing;
    }
};

exports._getSignedCookie = function (req, name, nothing, just) {
    return function () {
        if (req.signedCookies[name] != null) {
            return just(req.signedCookies[name]);
        }
        return nothing;
    };
};

exports._getHeader = function (req, field, nothing, just) {
    return function () {
        if (req.get(field) != null) {
            return just(req.get(field));
        }
        return nothing;
    };
};

exports._accepts = function (req, types, nothing, just) {
    return function () {
        if (req.accepts(types) != null) {
            return just(req.accepts(types));
        }
        return nothing;
    };
};

exports._acceptsCharset = function (req, charset, nothing, just) {
    return function () {
        if (req.acceptsCharset(charset) != null) {
            return just(req.acceptsCharset(charset));
        }
        return nothing;
    };
};

exports._acceptsLanguage = function (req, language, nothing, just) {
    return function () {
        if (req.acceptsLanguage(language) != null) {
            return just(req.acceptsLanguage(language));
        }
        return nothing;
    };
};

exports._hasType = function (req, type) {
    return function () {
        return req.is(type);
    };
};

exports._getRemoteIp = function (req) {
    return function () {
        return req.ip;
    };
};

exports._getRemoteIps = function (req) {
    return function () {
        return req.ips;
    };
};

exports._getPath = function (req) {
    return function () {
        return req.path;
    };
};

exports._getHostname = function (req) {
    return function () {
        return req.hostname;
    };
};

exports._getSubdomains = function (req) {
    return function () {
        return req.subdomains;
    };
};

exports._isFresh = function (req) {
    return function () {
        return req.fresh;
    };
};

exports._isStale = function (req) {
    return function () {
        return req.stale;
    };
};

exports._isXhr = function (req) {
    return function () {
        return req.xhr;
    };
};

exports._getProtocol = function (req) {
    return function () {
        return req.protocol;
    };
};

exports._getMethod = function (req) {
    return function () {
        return req.method;
    };
};

exports._getUrl = function (req) {
    return function () {
        return req.url;
    };
};

exports._getOriginalUrl = function (req) {
    return function () {
        return req.originalUrl;
    };
};

exports._getData = function (req, field, nothing, just) {
    return function () {
        if (req.userData && req.userData[field] != null) {
            return just(req.userData[field]);
        }
        return nothing;
    };
};

exports._setData = function (req, field, val) {
    return function () {
        req.userData = req.userData || {};
        req.userData[field] = val;
    };
};

<commit_msg>Check that param is not undefined in `req.body`<commit_after>// module Node.Express.Request

exports._getRouteParam = function (req, name, nothing, just) {
    return function () {
        if (req.params[name] != null) {
            return just(req.params[name]);
        }
        return nothing;
    };
};

exports._getRoute = function (req) {
    return function () {
        return req.route.path;
    };
};

exports._getBody = function (req) {
    return function () {
        return req.body;
    };
};

exports._getBodyParam = function (req, name, nothing, just) {
    return function () {
        if (req.body && name in req.body && req.body[name] != null) {
            return just(req.body[name]);
        }
        return nothing;
    };
};

exports._getQueryParam = function (req, name, nothing, just) {
    return function () {
        if (req.query && name in req.query && req.query[name] != null) {
            return just(req.query[name]);
        }
        return nothing;
    };
};

exports._getCookie = function (req, name, nothing, just) {
    return function () {
        if (req.cookies[name] != null) {
            return just(req.cookies[name]);
        }
        return nothing;
    }
};

exports._getSignedCookie = function (req, name, nothing, just) {
    return function () {
        if (req.signedCookies[name] != null) {
            return just(req.signedCookies[name]);
        }
        return nothing;
    };
};

exports._getHeader = function (req, field, nothing, just) {
    return function () {
        if (req.get(field) != null) {
            return just(req.get(field));
        }
        return nothing;
    };
};

exports._accepts = function (req, types, nothing, just) {
    return function () {
        if (req.accepts(types) != null) {
            return just(req.accepts(types));
        }
        return nothing;
    };
};

exports._acceptsCharset = function (req, charset, nothing, just) {
    return function () {
        if (req.acceptsCharset(charset) != null) {
            return just(req.acceptsCharset(charset));
        }
        return nothing;
    };
};

exports._acceptsLanguage = function (req, language, nothing, just) {
    return function () {
        if (req.acceptsLanguage(language) != null) {
            return just(req.acceptsLanguage(language));
        }
        return nothing;
    };
};

exports._hasType = function (req, type) {
    return function () {
        return req.is(type);
    };
};

exports._getRemoteIp = function (req) {
    return function () {
        return req.ip;
    };
};

exports._getRemoteIps = function (req) {
    return function () {
        return req.ips;
    };
};

exports._getPath = function (req) {
    return function () {
        return req.path;
    };
};

exports._getHostname = function (req) {
    return function () {
        return req.hostname;
    };
};

exports._getSubdomains = function (req) {
    return function () {
        return req.subdomains;
    };
};

exports._isFresh = function (req) {
    return function () {
        return req.fresh;
    };
};

exports._isStale = function (req) {
    return function () {
        return req.stale;
    };
};

exports._isXhr = function (req) {
    return function () {
        return req.xhr;
    };
};

exports._getProtocol = function (req) {
    return function () {
        return req.protocol;
    };
};

exports._getMethod = function (req) {
    return function () {
        return req.method;
    };
};

exports._getUrl = function (req) {
    return function () {
        return req.url;
    };
};

exports._getOriginalUrl = function (req) {
    return function () {
        return req.originalUrl;
    };
};

exports._getData = function (req, field, nothing, just) {
    return function () {
        if (req.userData && req.userData[field] != null) {
            return just(req.userData[field]);
        }
        return nothing;
    };
};

exports._setData = function (req, field, val) {
    return function () {
        req.userData = req.userData || {};
        req.userData[field] = val;
    };
};

<|endoftext|>