<commit_before>var json = require("json");
var geom = require("geoscript/geom");
var geotools = Packages.org.geotools;
var GeometryDescriptor = Packages.org.opengis.feature.type.GeometryDescriptor;
var SimpleFeatureBuilder = geotools.feature.simple.SimpleFeatureBuilder;
var CRS = geotools.referencing.CRS;

var FeatureType = function(config) {
    config = config || {};
    if (config.ft) {
        this._ft = config.ft;
        this.name = config.ft.localName;
        this._ft.attributeDescriptors.forEach(function(descriptor) {
            var att = {
                name: descriptor.localName,
                type: descriptor.type.binding
            };
            if (descriptor instanceof GeometryDescriptor) {
                // geomtry attribute, look for crs
                var crs = descriptor.type.coordinateReferenceSystem;
                if (crs) {
                    att.srs = CRS.toSRS(crs);
                    att.epsg = CRS.lookupEpsgCode(crs, true);
                }
                if (!this.geom) {
                    // first geometry is the default geometry
                    this.geom = att;
                }
            }
            if (!this.geom || !(descriptor instanceof GeometryDescriptor)) {
                this.atts[att.name] = att;
            }
        }, this);
    } else {
        this.name = config.name;
        this.geom = config.geom;
        this.atts = {};
        // check for geometry in attributes
        if (config.atts) {
            config.atts.forEach(function(att) {
                if (!this.geom && (att.type === geom.Geometry || att.type.prototype instanceof geom.Geometry)) {
                    this.geom = att;
                } else {
                    this.atts[att.name] = att;
                }
            }, this);
        }
    }

};

var Feature = function(config) {
    config = config || {};

    this.id = config.id;
    if (!config.id) {
        // TODO: generate a unique id
    }

    if (config.ftype) {
        this.ftype = config.ftype;
    } else {
        // TODO: generate feature type from attributes
    }

    if (config.feature) {
        this._feature = config.feature;
        if (this.ftype) {
            for (var name in this.ftype.atts) {
                if (ftype.geom && name === ftype.geom.name) {
                    this.geom = this._feature.getAttribute(name);
                } else {
                    this.atts[name] = this._feature.getAttribute(name);
                }
            }
        }
    }

    this.atts = {};
    for (var name in config.atts) {
        this.atts[name] = config.atts[name];
    }

    this.geom = config.geom;

};

Feature.prototype = {

    toString: function() {
        return json.encode(this.atts) + "; " + this.geom;
    },

    set: function(name, value) {
        this.atts[name] = value;
    },

    get: function(name) {
        return this.atts[name];
    },

    _sync: function() {
        if (!this._feature) {
            var builder = new SimpleFeatureBuilder(this.ftype._ft);
            this._feature = builder.buildFeature(null, []);
        }
        for (var name in this.atts) {
            this._feature.setAttribute(name, this.atts[name]);
        }
        if (this.geom) {
            this._feature.setDefaultGeometry(this.geom);
        }
    }

};

exports.FeatureType = FeatureType;
exports.Feature = Feature;
<commit_msg>New FeatureType implementation.<commit_after>var json = require("json");
var geom = require("geoscript/geom");
var proj = require("geoscript/proj");
var geotools = Packages.org.geotools;
var jts = Packages.org.vividsolutions.jts;
var GeometryDescriptor = Packages.org.opengis.feature.type.GeometryDescriptor;
var SimpleFeatureBuilder = geotools.feature.simple.SimpleFeatureBuilder;
var SimpleFeatureTypeBuilder = geotools.feature.simple.SimpleFeatureTypeBuilder;
var crs = geotools.referencing.CRS;
var NameImpl = geotools.feature.NameImpl;
var CoordinateReferenceSystem = Packages.org.opengis.referencing.crs.CoordinateReferenceSystem;

var types = {};
var typeNames = {};

// map type names to java types and
var javaTypeNames = ["String", "Integer", "Short", "Float", "Long", "Double"];
javaTypeNames.forEach(function(str) {
    var type = java.lang[str];
    types[str] = type;
    types[type.toString()] = type;
    typeNames[type.toString()] = str;
});
// map type names to jts geometry types
var jtsTypeNames = ["Geometry", "Point", "LineString", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon"];
jtsTypeNames.forEach(function(str) {
    var type = jts.geom[str];
    types[str] = type;
    types[type.toString()] = type;
    typeNames[type.toString()] = str;
});

var FeatureType = function(name, config) {
    config = config || {};
    this.ft = config.ft;
    if (name && config.atts) {
        // generate gt feature type from attributes
        var tb = new SimpleFeatureTypeBuilder();
        tb.setName(new NameImpl(config.name));
        atts.forEach(function(att) {
            var name = att[0],
                type = att[1];
            if (geom[type]) {
                if (att.length > 2) {
                    var xrs = att[2];
                    if (xrs instanceof CoordinateReferenceSystem) {
                        tb.crs(xrs);
                    } else {
                        tb.srs(xrs);
                    }
                }
            }
            tb.add(name, types[type]);
        });
        this.ft = tb.buildFeatureType();
    }
    this.name = this.ft.name.localPart;

    // geom property
    var gd = this.ft.geometryDescriptor;
    if (gd) {
        this.geom = [
            gd.localName,
            typeNames[gd.type.binding]
        ];
        if (gd.coordinateReferenceSystem) {
            this.geom[2] = gd.coordinateReferenceSystem;
        }
    }

    // atts property
    this.atts = this.ft.attributeDescriptors;

    // attnames property
    this.attnames = this.ft.attributeDescriptors;

};

FeatureType.prototype = {
    feature: function(atts, id) {
        return new Feture({
            ftype: this,
            atts: atts,
            id: id
        });
    }
};

var Feature = function(config) {
    config = config || {};

    this.id = config.id;
    if (!config.id) {
        // TODO: generate a unique id
    }

    if (config.ftype) {
        this.ftype = config.ftype;
    } else {
        // TODO: generate feature type from attributes
    }

    if (config.feature) {
        this._feature = config.feature;
        if (this.ftype) {
            for (var name in this.ftype.atts) {
                if (ftype.geom && name === ftype.geom.name) {
                    this.geom = this._feature.getAttribute(name);
                } else {
                    this.atts[name] = this._feature.getAttribute(name);
                }
            }
        }
    }

    this.atts = {};
    for (var name in config.atts) {
        this.atts[name] = config.atts[name];
    }

    this.geom = config.geom;

};

Feature.prototype = {

    toString: function() {
        return json.encode(this.atts) + "; " + this.geom;
    },

    set: function(name, value) {
        this.atts[name] = value;
    },

    get: function(name) {
        return this.atts[name];
    },

    _sync: function() {
        if (!this._feature) {
            var builder = new SimpleFeatureBuilder(this.ftype._ft);
            this._feature = builder.buildFeature(null, []);
        }
        for (var name in this.atts) {
            this._feature.setAttribute(name, this.atts[name]);
        }
        if (this.geom) {
            this._feature.setDefaultGeometry(this.geom);
        }
    }

};

exports.FeatureType = FeatureType;
exports.Feature = Feature;
<|endoftext|>


<commit_before>var loopback = require('loopback');
var Q = require('q');
var traverse = require('traverse');

var config = require('../support/config');
var logger = require('../support/logger');

var indexes = require('./indexes');
var models = require('./models');
var Relations = require('./relations');

var JsonSchemaValidator = require('./json-schema-validator');
var Links = require('./links');
var modelPropertiesConverter = require('./model-properties-converter');
var readOnlyDefaultValuesHandler = require('./readonly-default-values-handler');
var RegistryModels = require('./registry-models');

var itemSchemaProperties = {
    collectionName: {type: 'string', required: true, id: true, generated: false}
};

var relationSchema = require('./relation-schema');
var ItemSchema = module.exports = config.Model.extend('item-schemas', itemSchemaProperties);

// fill in loopbackJsonSchema.init
ItemSchema.app = null;

ItemSchema.prototype.allLinks = function() {
    var links = new Links(this.defaultLinks(), this.relationLinks(), this.links);
    return links.all();
};

ItemSchema.validate('relations', function customValidator(err) {
    if (!this.relations) {
        return;
    }

    var validator = new JsonSchemaValidator(relationSchema.$schema);
    var validationResult = validator.validate(relationSchema, this);

    var errors = validationResult.items;
    var hasErrors = false;
    for (var error in errors) {
        this.errors.add(errors[error].property, errors[error].message, errors[error].code);
        hasErrors = true;
    }

    if (hasErrors) {
        err();
    }
}, { message: 'relations is invalid' });

ItemSchema.prototype.customLinks = function() {
    var links = new Links(this.defaultLinks(), this.relationLinks(), this.links);
    return links.custom();
};

ItemSchema.prototype.relationLinks = function() {
    var relationKey, relationLinks = [], relation;
    if (this.relations) {
        for (relationKey in this.relations) {
            relation = this.relations[relationKey];

            if (relation.type == 'belongsTo') {
                relationLinks.push({ rel: relationKey, href: '/'+relation.collectionName+'/{'+relation.foreignKey+'}'});
            } else {
                relationLinks.push({ rel: relationKey, href: '/'+this.collectionName+'/{id}/'+relationKey });
            }
        }
    }

    return relationLinks;
};

ItemSchema.prototype.defaultLinks = function() {
    var itemUrlTemplate = this.itemUrlTemplate();
    var collectionUrl = this.collectionUrl();
    var schemaUrl = this.url();
    return [
        { rel: 'self', href: itemUrlTemplate },
        { rel: 'item', href: itemUrlTemplate },
        { rel: 'create', method: 'POST', href: collectionUrl, schema: { $ref: schemaUrl } },
        { rel: 'update', method: 'PUT', href: itemUrlTemplate },
        { rel: 'delete', method: 'DELETE', href: itemUrlTemplate },
        { rel: 'parent', href: collectionUrl }
    ];
};

ItemSchema.prototype.url = function() {
    return ItemSchema.urlForCollectionName(this.collectionName);
};

ItemSchema.urlForCollectionName = function urlForCollectionName (collectionName) {
    return '/' + ItemSchema.pluralModelName + '/' + collectionName;
};

ItemSchema.urlV2ForCollectionName = function urlForCollectionName (collectionName) {
    return '/v2/' + ItemSchema.pluralModelName + '/' + collectionName;
};

ItemSchema.prototype.itemUrlTemplate = function() {
    return this.collectionUrl() + '/{id}';
};

ItemSchema.prototype.collectionUrl = function() {
    var path = '/' + this.collectionName;
    return path;
};

ItemSchema.prototype.update$schema = function() {
    if (!this.$schema) {
        this.$schema = 'http://json-schema.org/draft-04/hyper-schema#';
    }
};

ItemSchema.prototype.beforeRegisterLoopbackModel = function(app, JsonSchemaModel, callback) {
    var name = JsonSchemaModel.sharedClass.name;
    logger.debug('Entered beforeRegisterLoopbackModel', name);

    if (ItemSchema.app._registeredLoopbackHooks[name]) {
        logger.debug('Loopback model name: '+name+' is already registered');
    } else {
        ItemSchema.defineRemoteHooks(JsonSchemaModel);
        ItemSchema.app._registeredLoopbackHooks[name] = true;
    }

    callback();
};

ItemSchema.prototype.constructModel = function() {
    var indexes = this.indexes || {};
    var model = config.Model.extend(
        this.collectionName,
        modelProperties.call(this),
        { plural: this.collectionName, indexes: indexes }
    );

    model.cachedItemSchema = this;
    model.isV2 = false;
    addJsonSchemaValidation.call(this, model);
    ItemSchema.attachModel(model);

    return model;
};

ItemSchema.prototype.modelV2UrlPath = function(tenantId) {
    // tenant not accept tenantID in url
    if (this.collectionName === 'tenants') {
        return 'v2/tenants';
    }
    if (tenantId) {
        return `v2/${this.collectionName}/${tenantId}`;
    } else {
        return `v2/${this.collectionName}/:tenantId`;
    }
};

ItemSchema.prototype.modelV2className = function(tenantId) {
    if (tenantId) {
        return `v2:${this.collectionName}:${tenantId}`;
    } else {
        return `v2:${this.collectionName}`;
    }
};

ItemSchema.prototype.constructModelV2 = function(tenantId) {
    var indexes = this.indexes || {};
    var model = config.Model.extend(
        this.collectionName,
        modelProperties.call(this),
        { plural: this.collectionName, indexes, http: { path: this.modelV2UrlPath(tenantId) } }
    );
    model.sharedClass.name = this.modelV2className(tenantId);
    model.cachedItemSchema = this;
    model.isV2 = true;

    if (tenantId) {
        model.__tenantId = tenantId;
    } else {
        model.__tenantId = 'default';
    }

    addJsonSchemaValidation.call(this, model);
    ItemSchema.attachModel(model);

    return model;
};

ItemSchema.prototype.createIndexes = function(callback) {
    indexes.create(this.getDataSource(), this.collectionName, callback);
};

ItemSchema.prototype.model = function() {
    return loopback.findModel(this.collectionName) || null;
};

ItemSchema.prototype.collectionSchema = function() {
    return new config.CollectionSchemaClass(this);
};

ItemSchema.findByCollectionName = function(collectionName, callback) {
    ItemSchema.findOne({ where: { collectionName: collectionName }}, function(err, jsonSchema) {
        if (err) {
            logger.error('Error fetching JSON Schema for collectionName:', collectionName, 'Error:', err);
        } else if (jsonSchema === null) {
            logger.warn('JSON Schema for collectionName', collectionName, 'not found.');
        }
        callback(err, jsonSchema);
    });
};

ItemSchema.preLoadedModels = false;

ItemSchema.preLoadModels = function(currentAttemptNumber) {
    if (currentAttemptNumber === undefined) {
        currentAttemptNumber = 0;
    }

    ItemSchema.find({}, function(err, itemSchemas) {
        if (err) {
            if (currentAttemptNumber >= config.registerItemSchemaMaxAttempts) {
                ItemSchema.app.emit('loadModels', err);
            } else {
                logger.error('Attempt: '+currentAttemptNumber+', find all itemschemas error: ' + err.message);

                setTimeout(function() {
                    ItemSchema.preLoadModels(currentAttemptNumber + 1);
                }, config.registerItemSchemaAttemptDelay);
            }

            return;
        }

        var promises = [];

        registryModels = new RegistryModels;

        for(var i = 0; i < itemSchemas.length; i++) {
            var schema = itemSchemas[i];

            promises = promises.concat(schema.prepareRegisterModel(false));
        }

        Q.allSettled(promises).done(function() {
            logger.info('Pre loaded all models');
            ItemSchema.preLoadedModels = true;
            ItemSchema.app.emit('loadModels', null);
        });
    });
};

ItemSchema.prototype.modelTenantsPool = function(model) {
    return config.modelTenantsPool[model] || [];
};

ItemSchema.prototype.associateModel = function(model, callback) {
    Relations.getInstance().bindRelation(this, model);
    callback();
};

ItemSchema.prototype.registerModel = function(model, callback) {
    var schema = this;
    this.beforeRegisterLoopbackModel(ItemSchema.app, model, function(err) {
        if (err) { return callback(err); }

        schema.associateModel(model, function(err) {
            if (err) { return callback(err); }
            ItemSchema.app.model(model);
            callback(null);
        });

    });
};

ItemSchema.prototype.prepareRegisterModel = function(afterSave) {
    var promises = [];

    registryModels = new RegistryModels();

    var model = this.constructModel();
    registryModels.appendModelV1(this.collectionName, model);

    var modelV2 = this.constructModelV2();
    registryModels.appendModelV2(this.collectionName, 'default', modelV2);

    ItemSchema.attachModel(model);
    promises.push(ItemSchema.executeRegisterModel(this, model, afterSave));

    ItemSchema.attachModel(modelV2);
    promises.push(ItemSchema.executeRegisterModel(this, modelV2, afterSave));

    var tenantsPoolMap = this.modelTenantsPool(model);

    tenantsPoolMap.forEach((tenantId) => {
        var scopedModel = this.constructModelV2(tenantId);
        registryModels.appendModelV2(this.collectionName, tenantId, scopedModel);
        scopedModel.tenantId = tenantId;
        ItemSchema.attachModel(scopedModel);
        promises.push(ItemSchema.executeRegisterModel(this, scopedModel, afterSave));
    });

    return promises;
};

ItemSchema.executeRegisterModel = function(schema, model, afterSave) {
    var deferred = Q.defer();
    var collectionName = schema.collectionName;

    schema.registerModel(model, function(err) {
        if (err) {
            logger.error('Failed to register model '+collectionName+': ' + err);
        } else {
            logger.debug('registered model: '+collectionName);
        }

        model.sharedClass.name = model.modelName; // to before and after work on v2 model

        if (afterSave) {
            schema.links = schema.allLinks();
        }

        deferred.resolve();
    });

    return deferred;
};

ItemSchema.attachModel = function(model) {
    model.attachTo(ItemSchema.dataSource);
};

ItemSchema.afterInitialize = function() {
    modelPropertiesConverter.restore(this);
    this.__customLinks = this.links;
    this.links = this.allLinks();
};

ItemSchema.beforeSave = function(next, data) {
    this.update$schema();
    this.links = this.customLinks();
    modelPropertiesConverter.convert(data);
    next();
};

ItemSchema.observe('after save', function afterSave(ctx, next) {
    var schema = ctx.instance;
    modelPropertiesConverter.restore(schema);

    var promises = schema.prepareRegisterModel(true);

    Q.allSettled(promises).done(function() {
        schema.createIndexes(function(err) {
            next.call(err);
        });
    });

    // THE COMMENTED CODE BELOW WORKS FOR V1
    // ON BRANCH MASTER. IF THE CODE ABOVE
    // STARTS TO WORK ON V1 AND V2, WE CAN
    // DELETE THESE COMMENTED LINES!

    // var model = schema.constructModel();
    // ItemSchema.attachModel(model);

    // schema.registerModel(model, function(err) {
    //     if (err) {
    //         logger.error('Failed to register model '+ schema.collectionName+': ' + err);
    //     } else {
    //         logger.debug('registered model: '+schema.collectionName);
    //     }
    //     schema.links = schema.allLinks();
    // });

    // schema.createIndexes(function(err) {
    //     next(err);
    // });
});

ItemSchema.defaultRemoteHookInitializers = [
    includeBodyFieldInjector
];

ItemSchema.remoteHookInitializers = [];

ItemSchema.defineRemoteHooks = function(model) {
    logger.debug('Defining remote hooks for ' + model.modelName);

    var hooksInitializers = ItemSchema.remoteHookInitializers;

    for (var i=0; i<hooksInitializers.length; i++) {
        logger.debug('Defining remote hook ' + hooksInitializers[i].name);
        hooksInitializers[i].call(null, model);
    }
};

ItemSchema.registerRemoteHookInitializers = function(hooks) {
    if (hooks instanceof Array) {
        ItemSchema.remoteHookInitializers = ItemSchema.remoteHookInitializers.concat(hooks);
    } else {
        ItemSchema.remoteHookInitializers.push(hooks);
    }
 };

function addJsonSchemaValidation(modelClass) {
    var itemSchema = this;
    modelClass.validate('_all', function customValidator(err) {
        var validator = new JsonSchemaValidator(itemSchema.$schema);
        var validationResult = validator.validate(itemSchema, this);

        var errors = validationResult.items;
        for (var error in errors) {
            this.errors.add(errors[error].property, errors[error].message, errors[error].code);
        }

        if (Object.keys(this.errors.codes).length > 0) {
            err();
        }
    }, { message: 'Instance is invalid' });
}

function modelProperties() {
    var type;
    var properties = traverse(this.properties).map(function(property) {
        if (this.node) {
            delete this.node.required;
        }

        type = traverse(property).get(['type']);
        if (type === 'array') {
            this.remove();
        }
        if (type === 'integer') {
            this.node.type = 'number';
        }
    });

    if (!config.generatedId && properties) {
        properties.id = {type: 'string', generated: false, id: true};
    }

    return properties;
}

function includeBodyFieldInjector (model) {
    logger.debug('include field body injector for model: ', model.modelName);
    model.beforeRemote('**', function(ctx, user, next) {
        readOnlyDefaultValuesHandler(ctx);
        next();
    });
}
<commit_msg>Fix order to attachmodel to make he db pool work<commit_after>var loopback = require('loopback');
var Q = require('q');
var traverse = require('traverse');

var config = require('../support/config');
var logger = require('../support/logger');

var indexes = require('./indexes');
var models = require('./models');
var Relations = require('./relations');

var JsonSchemaValidator = require('./json-schema-validator');
var Links = require('./links');
var modelPropertiesConverter = require('./model-properties-converter');
var readOnlyDefaultValuesHandler = require('./readonly-default-values-handler');
var RegistryModels = require('./registry-models');

var itemSchemaProperties = {
    collectionName: {type: 'string', required: true, id: true, generated: false}
};

var relationSchema = require('./relation-schema');
var ItemSchema = module.exports = config.Model.extend('item-schemas', itemSchemaProperties);

// fill in loopbackJsonSchema.init
ItemSchema.app = null;

ItemSchema.prototype.allLinks = function() {
    var links = new Links(this.defaultLinks(), this.relationLinks(), this.links);
    return links.all();
};

ItemSchema.validate('relations', function customValidator(err) {
    if (!this.relations) {
        return;
    }

    var validator = new JsonSchemaValidator(relationSchema.$schema);
    var validationResult = validator.validate(relationSchema, this);

    var errors = validationResult.items;
    var hasErrors = false;
    for (var error in errors) {
        this.errors.add(errors[error].property, errors[error].message, errors[error].code);
        hasErrors = true;
    }

    if (hasErrors) {
        err();
    }
}, { message: 'relations is invalid' });

ItemSchema.prototype.customLinks = function() {
    var links = new Links(this.defaultLinks(), this.relationLinks(), this.links);
    return links.custom();
};

ItemSchema.prototype.relationLinks = function() {
    var relationKey, relationLinks = [], relation;
    if (this.relations) {
        for (relationKey in this.relations) {
            relation = this.relations[relationKey];

            if (relation.type == 'belongsTo') {
                relationLinks.push({ rel: relationKey, href: '/'+relation.collectionName+'/{'+relation.foreignKey+'}'});
            } else {
                relationLinks.push({ rel: relationKey, href: '/'+this.collectionName+'/{id}/'+relationKey });
            }
        }
    }

    return relationLinks;
};

ItemSchema.prototype.defaultLinks = function() {
    var itemUrlTemplate = this.itemUrlTemplate();
    var collectionUrl = this.collectionUrl();
    var schemaUrl = this.url();
    return [
        { rel: 'self', href: itemUrlTemplate },
        { rel: 'item', href: itemUrlTemplate },
        { rel: 'create', method: 'POST', href: collectionUrl, schema: { $ref: schemaUrl } },
        { rel: 'update', method: 'PUT', href: itemUrlTemplate },
        { rel: 'delete', method: 'DELETE', href: itemUrlTemplate },
        { rel: 'parent', href: collectionUrl }
    ];
};

ItemSchema.prototype.url = function() {
    return ItemSchema.urlForCollectionName(this.collectionName);
};

ItemSchema.urlForCollectionName = function urlForCollectionName (collectionName) {
    return '/' + ItemSchema.pluralModelName + '/' + collectionName;
};

ItemSchema.urlV2ForCollectionName = function urlForCollectionName (collectionName) {
    return '/v2/' + ItemSchema.pluralModelName + '/' + collectionName;
};

ItemSchema.prototype.itemUrlTemplate = function() {
    return this.collectionUrl() + '/{id}';
};

ItemSchema.prototype.collectionUrl = function() {
    var path = '/' + this.collectionName;
    return path;
};

ItemSchema.prototype.update$schema = function() {
    if (!this.$schema) {
        this.$schema = 'http://json-schema.org/draft-04/hyper-schema#';
    }
};

ItemSchema.prototype.beforeRegisterLoopbackModel = function(app, JsonSchemaModel, callback) {
    var name = JsonSchemaModel.sharedClass.name;
    logger.debug('Entered beforeRegisterLoopbackModel', name);

    if (ItemSchema.app._registeredLoopbackHooks[name]) {
        logger.debug('Loopback model name: '+name+' is already registered');
    } else {
        ItemSchema.defineRemoteHooks(JsonSchemaModel);
        ItemSchema.app._registeredLoopbackHooks[name] = true;
    }

    callback();
};

ItemSchema.prototype.constructModel = function() {
    var indexes = this.indexes || {};
    var model = config.Model.extend(
        this.collectionName,
        modelProperties.call(this),
        { plural: this.collectionName, indexes: indexes }
    );

    model.cachedItemSchema = this;
    model.isV2 = false;
    addJsonSchemaValidation.call(this, model);
    ItemSchema.attachModel(model);

    return model;
};

ItemSchema.prototype.modelV2UrlPath = function(tenantId) {
    // tenant not accept tenantID in url
    if (this.collectionName === 'tenants') {
        return 'v2/tenants';
    }
    if (tenantId) {
        return `v2/${this.collectionName}/${tenantId}`;
    } else {
        return `v2/${this.collectionName}/:tenantId`;
    }
};

ItemSchema.prototype.modelV2className = function(tenantId) {
    if (tenantId) {
        return `v2:${this.collectionName}:${tenantId}`;
    } else {
        return `v2:${this.collectionName}`;
    }
};

ItemSchema.prototype.constructModelV2 = function(tenantId) {
    var indexes = this.indexes || {};
    var model = config.Model.extend(
        this.collectionName,
        modelProperties.call(this),
        { plural: this.collectionName, indexes, http: { path: this.modelV2UrlPath(tenantId) } }
    );
    model.sharedClass.name = this.modelV2className(tenantId);
    model.cachedItemSchema = this;
    model.isV2 = true;

    if (tenantId) {
        model.__tenantId = tenantId;
    } else {
        model.__tenantId = 'default';
    }

    addJsonSchemaValidation.call(this, model);
    ItemSchema.attachModel(model);

    return model;
};

ItemSchema.prototype.createIndexes = function(callback) {
    indexes.create(this.getDataSource(), this.collectionName, callback);
};

ItemSchema.prototype.model = function() {
    return loopback.findModel(this.collectionName) || null;
};

ItemSchema.prototype.collectionSchema = function() {
    return new config.CollectionSchemaClass(this);
};

ItemSchema.findByCollectionName = function(collectionName, callback) {
    ItemSchema.findOne({ where: { collectionName: collectionName }}, function(err, jsonSchema) {
        if (err) {
            logger.error('Error fetching JSON Schema for collectionName:', collectionName, 'Error:', err);
        } else if (jsonSchema === null) {
            logger.warn('JSON Schema for collectionName', collectionName, 'not found.');
        }
        callback(err, jsonSchema);
    });
};

ItemSchema.preLoadedModels = false;

ItemSchema.preLoadModels = function(currentAttemptNumber) {
    if (currentAttemptNumber === undefined) {
        currentAttemptNumber = 0;
    }

    ItemSchema.find({}, function(err, itemSchemas) {
        if (err) {
            if (currentAttemptNumber >= config.registerItemSchemaMaxAttempts) {
                ItemSchema.app.emit('loadModels', err);
            } else {
                logger.error('Attempt: '+currentAttemptNumber+', find all itemschemas error: ' + err.message);

                setTimeout(function() {
                    ItemSchema.preLoadModels(currentAttemptNumber + 1);
                }, config.registerItemSchemaAttemptDelay);
            }

            return;
        }

        var promises = [];

        registryModels = new RegistryModels;

        for(var i = 0; i < itemSchemas.length; i++) {
            var schema = itemSchemas[i];

            promises = promises.concat(schema.prepareRegisterModel(false));
        }

        Q.allSettled(promises).done(function() {
            logger.info('Pre loaded all models');
            ItemSchema.preLoadedModels = true;
            ItemSchema.app.emit('loadModels', null);
        });
    });
};

ItemSchema.prototype.modelTenantsPool = function(model) {
    return config.modelTenantsPool[model] || [];
};

ItemSchema.prototype.associateModel = function(model, callback) {
    Relations.getInstance().bindRelation(this, model);
    callback();
};

ItemSchema.prototype.registerModel = function(model, callback) {
    var schema = this;
    this.beforeRegisterLoopbackModel(ItemSchema.app, model, function(err) {
        if (err) { return callback(err); }

        schema.associateModel(model, function(err) {
            if (err) { return callback(err); }
            ItemSchema.app.model(model);
            callback(null);
        });

    });
};

ItemSchema.prototype.prepareRegisterModel = function(afterSave) {
    var promises = [];

    registryModels = new RegistryModels();

    var model = this.constructModel();
    registryModels.appendModelV1(this.collectionName, model);

    var modelV2 = this.constructModelV2();
    registryModels.appendModelV2(this.collectionName, 'default', modelV2);

    ItemSchema.attachModel(model);
    promises.push(ItemSchema.executeRegisterModel(this, model, afterSave));

    var tenantsPoolMap = this.modelTenantsPool(model);

    tenantsPoolMap.forEach((tenantId) => {
        var scopedModel = this.constructModelV2(tenantId);
        registryModels.appendModelV2(this.collectionName, tenantId, scopedModel);
        scopedModel.tenantId = tenantId;
        ItemSchema.attachModel(scopedModel);
        promises.push(ItemSchema.executeRegisterModel(this, scopedModel, afterSave));
    });

    ItemSchema.attachModel(modelV2);
    promises.push(ItemSchema.executeRegisterModel(this, modelV2, afterSave));

    return promises;
};

ItemSchema.executeRegisterModel = function(schema, model, afterSave) {
    var deferred = Q.defer();
    var collectionName = schema.collectionName;

    schema.registerModel(model, function(err) {
        if (err) {
            logger.error('Failed to register model '+collectionName+': ' + err);
        } else {
            logger.debug('registered model: '+collectionName);
        }

        model.sharedClass.name = model.modelName; // to before and after work on v2 model

        if (afterSave) {
            schema.links = schema.allLinks();
        }

        deferred.resolve();
    });

    return deferred;
};

ItemSchema.attachModel = function(model) {
    model.attachTo(ItemSchema.dataSource);
};

ItemSchema.afterInitialize = function() {
    modelPropertiesConverter.restore(this);
    this.__customLinks = this.links;
    this.links = this.allLinks();
};

ItemSchema.beforeSave = function(next, data) {
    this.update$schema();
    this.links = this.customLinks();
    modelPropertiesConverter.convert(data);
    next();
};

ItemSchema.observe('after save', function afterSave(ctx, next) {
    var schema = ctx.instance;
    modelPropertiesConverter.restore(schema);

    var promises = schema.prepareRegisterModel(true);

    Q.allSettled(promises).done(function() {
        schema.createIndexes(function(err) {
            next.call(err);
        });
    });

    // THE COMMENTED CODE BELOW WORKS FOR V1
    // ON BRANCH MASTER. IF THE CODE ABOVE
    // STARTS TO WORK ON V1 AND V2, WE CAN
    // DELETE THESE COMMENTED LINES!

    // var model = schema.constructModel();
    // ItemSchema.attachModel(model);

    // schema.registerModel(model, function(err) {
    //     if (err) {
    //         logger.error('Failed to register model '+ schema.collectionName+': ' + err);
    //     } else {
    //         logger.debug('registered model: '+schema.collectionName);
    //     }
    //     schema.links = schema.allLinks();
    // });

    // schema.createIndexes(function(err) {
    //     next(err);
    // });
});

ItemSchema.defaultRemoteHookInitializers = [
    includeBodyFieldInjector
];

ItemSchema.remoteHookInitializers = [];

ItemSchema.defineRemoteHooks = function(model) {
    logger.debug('Defining remote hooks for ' + model.modelName);

    var hooksInitializers = ItemSchema.remoteHookInitializers;

    for (var i=0; i<hooksInitializers.length; i++) {
        logger.debug('Defining remote hook ' + hooksInitializers[i].name);
        hooksInitializers[i].call(null, model);
    }
};

ItemSchema.registerRemoteHookInitializers = function(hooks) {
    if (hooks instanceof Array) {
        ItemSchema.remoteHookInitializers = ItemSchema.remoteHookInitializers.concat(hooks);
    } else {
        ItemSchema.remoteHookInitializers.push(hooks);
    }
 };

function addJsonSchemaValidation(modelClass) {
    var itemSchema = this;
    modelClass.validate('_all', function customValidator(err) {
        var validator = new JsonSchemaValidator(itemSchema.$schema);
        var validationResult = validator.validate(itemSchema, this);

        var errors = validationResult.items;
        for (var error in errors) {
            this.errors.add(errors[error].property, errors[error].message, errors[error].code);
        }

        if (Object.keys(this.errors.codes).length > 0) {
            err();
        }
    }, { message: 'Instance is invalid' });
}

function modelProperties() {
    var type;
    var properties = traverse(this.properties).map(function(property) {
        if (this.node) {
            delete this.node.required;
        }

        type = traverse(property).get(['type']);
        if (type === 'array') {
            this.remove();
        }
        if (type === 'integer') {
            this.node.type = 'number';
        }
    });

    if (!config.generatedId && properties) {
        properties.id = {type: 'string', generated: false, id: true};
    }

    return properties;
}

function includeBodyFieldInjector (model) {
    logger.debug('include field body injector for model: ', model.modelName);
    model.beforeRemote('**', function(ctx, user, next) {
        readOnlyDefaultValuesHandler(ctx);
        next();
    });
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2018 The Closure Library Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 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.
 */

const {execute} = require('../../bin/closuremakedeps');
const path = require('path');
const fs = require('fs');
const jasmineDiff = require('jasmine-diff');
const os = require('os');

// This test isn't that slow unless you're debugging.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;

// By default assume this is running based on the structure of the git repo.
// closure_deps/ lives in the save repo as Closure, and the library's code is in
// closure/goog and third_party/closure/goog.
const CLOSURE_SUB_DIR = process.env.CLOSURE_SUB_DIR || 'closure/goog';
const CLOSURE_LIBRARY_PATH =
    process.env.CLOSURE_LIBRARY_PATH || path.resolve(__dirname, '../../../');

const CLOSURE_PATH = path.resolve(CLOSURE_LIBRARY_PATH, CLOSURE_SUB_DIR);
const THIRD_PARTY_PATH =
    path.resolve(CLOSURE_LIBRARY_PATH, 'third_party', CLOSURE_SUB_DIR);

/**
 * @param {string} flag
 * @param {string} value
 * @return {!Array<string>}
 */
function flag(flag, value) {
  return [flag, value];
}

/**
 * @param {string} p
 * @return {!Array<string>}
 */
function exclude(p) {
  return flag('--exclude', path.resolve(CLOSURE_PATH, p));
}

/**
 * Skips lines for test files, as those are not present in the npm package for
 * the library.
 *
 *
 * @param {string} line
 * @return {boolean}
 */
function skipTests(line) {
  return !/goog\.addDependency\('[^']*_test\.js/.test(line);
}

describe('closure-make-deps', function() {
  const tempFile = path.join(os.tmpdir(), 'closuremakejsdepstmp.js');
  const tempFileRelativePath = path.relative(CLOSURE_PATH, tempFile);

  const closureDepsContents =
      fs.readFileSync(path.resolve(CLOSURE_PATH, 'deps.js'), {
          encoding: 'utf8'
        }).replace(/^(\/\/.*)?\n/gm, '');

  beforeEach(function() {
    jasmine.addMatchers(jasmineDiff(jasmine, {
      colors: false,
      inline: false,
    }));
  });

  afterEach(() => {
    if (fs.existsSync(tempFile)) {
      fs.unlinkSync(tempFile);
    }
  });

  it('produces closure library deps file', async function() {
    const flags = [
      ...flag('--root', CLOSURE_PATH),
      ...flag('--root', THIRD_PARTY_PATH),
      ...exclude('deps.js'),
      ...exclude('transpile.js'),
      ...exclude('testing/testdata'),
      ...exclude('bin'),
      ...exclude('conformance'),
      ...exclude('css'),
      ...exclude('demos'),
      ...exclude('docs'),
      ...exclude('transpile'),
      ...exclude('debug_loader_integration_tests'),
      ...exclude('base_debug_loader_test.js'),
    ];

    const result = await execute(flags);
    expect(result.errors.filter(e => e.fatal).map(e => e.toString()))
        .toEqual([]);

    let resultLines = result.text.split('\n');
    resultLines = resultLines.filter(skipTests);

    let expectedLines = closureDepsContents.split('\n');
    expectedLines = expectedLines.filter(skipTests);

    expect(resultLines).toEqual(expectedLines);
  });

  it('merge only deps produces same file', async function() {
    const flags = [
      ...flag('--merge-deps', 'true'),
      ...flag('--closure-path', CLOSURE_PATH),
      ...flag('-f', path.join(CLOSURE_PATH, 'deps.js')),
    ];

    const result = await execute(flags);
    expect(result.errors).toEqual([]);
    expect(result.text).toEqual(closureDepsContents);
  });

  it('input deps file forward declares symbols', async function() {
    fs.writeFileSync(
        tempFile, `goog.module('ex');\ngoog.require('goog.array');`);

    const flags = [
      ...flag('--closure-path', CLOSURE_PATH),
      ...flag('-f', path.join(CLOSURE_PATH, 'deps.js')),
      ...flag('-f', tempFile)
    ];

    const result = await execute(flags);
    expect(result.errors).toEqual([]);
    expect(result.text)
        .toEqual(
            `goog.addDependency('${tempFileRelativePath}'` +
            `, ['ex'], ['goog.array'], {'module': 'goog'});\n`);
  });

  it('missing require is error', async function() {
    fs.writeFileSync(tempFile, `goog.require('goog.array');`);

    const flags = [
      ...flag('--closure-path', CLOSURE_PATH),
      ...flag('-f', tempFile)
    ];

    try {
      await execute(flags);
      fail();
    } catch (error) {
      expect(error.toString())
          .toEqual(
              `Error: Error in source file "${tempFile}": ` +
              `Could not find "goog.array".`);
    }
  });

  it('merge deps and input file', async function() {
    fs.writeFileSync(tempFile, `goog.module('a.b');`);

    const expectedExtraFileLine = `goog.addDependency('${
        tempFileRelativePath}', ['a.b'], [], {'module': 'goog'});\n`;

    const expectedContents = expectedExtraFileLine + closureDepsContents;

    const flags = [
      ...flag('--merge-deps', 'true'),
      ...flag('--closure-path', CLOSURE_PATH),
      ...flag('-f', tempFile),
      ...flag('-f', path.join(CLOSURE_PATH, 'deps.js')),
    ];

    const result = await execute(flags);
    expect(result.errors).toEqual([]);
    expect(result.text).toEqual(expectedContents);
  });
});
<commit_msg>Sort deps lines in closuremakedeps_test.<commit_after>/**
 * @license
 * Copyright 2018 The Closure Library Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 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.
 */

const {execute} = require('../../bin/closuremakedeps');
const path = require('path');
const fs = require('fs');
const jasmineDiff = require('jasmine-diff');
const os = require('os');

// This test isn't that slow unless you're debugging.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;

// By default assume this is running based on the structure of the git repo.
// closure_deps/ lives in the save repo as Closure, and the library's code is in
// closure/goog and third_party/closure/goog.
const CLOSURE_SUB_DIR = process.env.CLOSURE_SUB_DIR || 'closure/goog';
const CLOSURE_LIBRARY_PATH =
    process.env.CLOSURE_LIBRARY_PATH || path.resolve(__dirname, '../../../');

const CLOSURE_PATH = path.resolve(CLOSURE_LIBRARY_PATH, CLOSURE_SUB_DIR);
const THIRD_PARTY_PATH =
    path.resolve(CLOSURE_LIBRARY_PATH, 'third_party', CLOSURE_SUB_DIR);

/**
 * @param {string} flag
 * @param {string} value
 * @return {!Array<string>}
 */
function flag(flag, value) {
  return [flag, value];
}

/**
 * @param {string} p
 * @return {!Array<string>}
 */
function exclude(p) {
  return flag('--exclude', path.resolve(CLOSURE_PATH, p));
}

/**
 * Skips lines for test files, as those are not present in the npm package for
 * the library.
 *
 *
 * @param {string} line
 * @return {boolean}
 */
function skipTests(line) {
  return !/goog\.addDependency\('[^']*_test\.js/.test(line);
}

/**
 * Sorts the lines of a string, since the specific order of the addDependency
 * lines is not strictly important. TODO(sdh): ideally we would not need to
 * do this, but due to some internal work moving these files, we end up with
 * a different order.  A better fix would be to simply use the same tool and
 * then the golden tests would be unnecessary.
 *
 * @param {string} str
 * @return {string}
 */
function sortLines(str) {
  return str.split('\n').sort().join('\n');
}

describe('closure-make-deps', function() {
  const tempFile = path.join(os.tmpdir(), 'closuremakejsdepstmp.js');
  const tempFileRelativePath = path.relative(CLOSURE_PATH, tempFile);

  const closureDepsContents =
      fs.readFileSync(path.resolve(CLOSURE_PATH, 'deps.js'), {
          encoding: 'utf8'
        }).replace(/^(\/\/.*)?\n/gm, '');

  beforeEach(function() {
    jasmine.addMatchers(jasmineDiff(jasmine, {
      colors: false,
      inline: false,
    }));
  });

  afterEach(() => {
    if (fs.existsSync(tempFile)) {
      fs.unlinkSync(tempFile);
    }
  });

  it('produces closure library deps file', async function() {
    const flags = [
      ...flag('--root', CLOSURE_PATH),
      ...flag('--root', THIRD_PARTY_PATH),
      ...exclude('deps.js'),
      ...exclude('transpile.js'),
      ...exclude('testing/testdata'),
      ...exclude('bin'),
      ...exclude('conformance'),
      ...exclude('css'),
      ...exclude('demos'),
      ...exclude('docs'),
      ...exclude('transpile'),
      ...exclude('debug_loader_integration_tests'),
      ...exclude('base_debug_loader_test.js'),
    ];

    const result = await execute(flags);
    expect(result.errors.filter(e => e.fatal).map(e => e.toString()))
        .toEqual([]);

    let resultLines = result.text.split('\n').sort();
    resultLines = resultLines.filter(skipTests);

    let expectedLines = closureDepsContents.split('\n').sort();
    expectedLines = expectedLines.filter(skipTests);

    expect(resultLines).toEqual(expectedLines);
  });

  it('merge only deps produces same file', async function() {
    const flags = [
      ...flag('--merge-deps', 'true'),
      ...flag('--closure-path', CLOSURE_PATH),
      ...flag('-f', path.join(CLOSURE_PATH, 'deps.js')),
    ];

    const result = await execute(flags);
    expect(result.errors).toEqual([]);
    expect(sortLines(result.text)).toEqual(sortLines(closureDepsContents));
  });

  it('input deps file forward declares symbols', async function() {
    fs.writeFileSync(
        tempFile, `goog.module('ex');\ngoog.require('goog.array');`);

    const flags = [
      ...flag('--closure-path', CLOSURE_PATH),
      ...flag('-f', path.join(CLOSURE_PATH, 'deps.js')),
      ...flag('-f', tempFile)
    ];

    const result = await execute(flags);
    expect(result.errors).toEqual([]);
    expect(result.text)
        .toEqual(
            `goog.addDependency('${tempFileRelativePath}'` +
            `, ['ex'], ['goog.array'], {'module': 'goog'});\n`);
  });

  it('missing require is error', async function() {
    fs.writeFileSync(tempFile, `goog.require('goog.array');`);

    const flags = [
      ...flag('--closure-path', CLOSURE_PATH),
      ...flag('-f', tempFile)
    ];

    try {
      await execute(flags);
      fail();
    } catch (error) {
      expect(error.toString())
          .toEqual(
              `Error: Error in source file "${tempFile}": ` +
              `Could not find "goog.array".`);
    }
  });

  it('merge deps and input file', async function() {
    fs.writeFileSync(tempFile, `goog.module('a.b');`);

    const expectedExtraFileLine = `goog.addDependency('${
        tempFileRelativePath}', ['a.b'], [], {'module': 'goog'});\n`;

    const expectedContents = expectedExtraFileLine + closureDepsContents;

    const flags = [
      ...flag('--merge-deps', 'true'),
      ...flag('--closure-path', CLOSURE_PATH),
      ...flag('-f', tempFile),
      ...flag('-f', path.join(CLOSURE_PATH, 'deps.js')),
    ];

    const result = await execute(flags);
    expect(result.errors).toEqual([]);
    expect(sortLines(result.text)).toEqual(sortLines(expectedContents));
  });
});
<|endoftext|>


<commit_before>#!/usr/bin/env nodejs

var express = require('express');
var app = express();
var request = require("request");  // To make HTTP requests at the server side

var server = require('http').Server(app);
var io = require('socket.io')(server);

var helmet = require('helmet');  // To change response headers

// To temporarily store JSON data from GitHub and also
// the number of connected users
var redis = require("redis"),
    redis_client = redis.createClient();

var path = require('path');

const logger = require('./logger');
const argv = require('minimist')(process.argv.slice(2));
const isDev = process.env.NODE_ENV !== 'production';

// Get the intended port number, use port 8000 if not provided
const port = argv.port || process.env.PORT || 8000;
server.listen(port, (err) => {
  if(err){
    return logger.error(err.message);
  }
});
if(isDev)
  logger.appStarted(port, 'http://localhost');
else
  logger.appStarted(port);

// Apply security middlewares
app.use(helmet());

// Remove x-powered-by header
app.disable('x-powered-by');

// server static files
app.use('/static', express.static('app'));

// Load main web page
app.get('/', function (req, res) {
  res.sendFile(path.resolve('app/index.html'));
});

var allClients = [];

// When a socket connection is created
io.on('connection', function (socket) {
  allClients.push(socket);
  redis_client.incr('connected_users');
  socket.on('disconnect', function() {
     logger.v('Got disconnect!');
     var i = allClients.indexOf(socket);
     allClients.splice(i, 1);
     redis_client.decr('connected_users');
  });
  socket.on('error', function(){
    logger.error('Got errored!');
    redis_client.decr('connected_users');
  })
});

// Function to get events from GitHub API
function fetchDataFromGithub(){
  var options = {
    url: 'https://api.github.com/events',
    headers: {
      'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36',
      'Authorization': 'token ' + process.env.GITHUB_OAUTH_KEY
    }
  };
  request(options, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      var data = JSON.parse(body);
      var stripedData = stripData(data);  // Keep only useful keys
      allClients.forEach(function(socket){
        if(socket != null && socket.connected == true){
            redis_client.get('connected_users', function(err, count) {
                if(!err && count != null){
                    socket.volatile.json.emit('github', {data: stripedData, connected_users: count});
                }else{
                  logger.error(err.message);
                }
            });
        }
      });

    }else{
      logger.error("GitHub status code: " + response.statusCode);
    }
  })
  setTimeout(fetchDataFromGithub, 2000);
}
setTimeout(fetchDataFromGithub, 2000);

function stripData(data){
  var stripedData = [];
  var pushEventCounter = 0;
  var IssueCommentEventCounter = 0;
  var IssuesEventCounter = 0;
  data.forEach(function(data){
    if(data.type == 'PushEvent'){
      if(pushEventCounter > 3) return;
      if(data.payload.size != 0){
        stripedData.push({
          'id': data.id,
          'type': data.type,
          'user': data.actor.display_login,
          'user_avatar': data.actor.avatar_url + 'v=3&s=64',
          'repo_id': data.repo.id,
          'repo_name': data.repo.name,
          'payload_size': data.payload.size,
          'message': data.payload.commits[0].message,
          'created': data.created_at
        });
        pushEventCounter++;
      }
    }else if(data.type == 'IssueCommentEvent'){
      stripedData.push({
        'id': data.id,
        'type': data.type,
        'user': data.actor.display_login,
        'user_avatar': data.actor.avatar_url + 'v=3&s=64',
        'repo_id': data.repo.id,
        'repo_name': data.repo.name,
        'payload_size': 0,
        'message': data.body,
        'created': data.created_at
      });
    }else if(data.type == 'PullRequestEvent'){
      stripedData.push({
        'id': data.id,
        'type': data.type,
        'user': data.actor.display_login,
        'user_avatar': data.actor.avatar_url + 'v=3&s=64',
        'repo_id': data.repo.id,
        'repo_name': data.repo.name,
        'action': data.payload.action,  // opened, reopened, closed, merged
        'message': data.payload.pull_request.title,
        'created': data.created_at
      });
    }else if(data.type == 'IssuesEvent'){
      stripedData.push({
        'id': data.id,
        'type': data.type,
        'user': data.actor.display_login,
        'user_avatar': data.actor.avatar_url + 'v=3&s=64',
        'repo_id': data.repo.id,
        'repo_name': data.repo.name,
        'action': data.payload.action,  // opened, reopened, closed
        'message': data.payload.issue.title,
        'created': data.created_at
      });
    }
  });
  return stripedData;
}
<commit_msg>Set `merged' action if `merged' key is true<commit_after>#!/usr/bin/env nodejs

var express = require('express');
var app = express();
var request = require("request");  // To make HTTP requests at the server side

var server = require('http').Server(app);
var io = require('socket.io')(server);

var helmet = require('helmet');  // To change response headers

// To temporarily store JSON data from GitHub and also
// the number of connected users
var redis = require("redis"),
    redis_client = redis.createClient();

var path = require('path');

const logger = require('./logger');
const argv = require('minimist')(process.argv.slice(2));
const isDev = process.env.NODE_ENV !== 'production';

// Get the intended port number, use port 8000 if not provided
const port = argv.port || process.env.PORT || 8000;
server.listen(port, (err) => {
  if(err){
    return logger.error(err.message);
  }
});
if(isDev)
  logger.appStarted(port, 'http://localhost');
else
  logger.appStarted(port);

// Apply security middlewares
app.use(helmet());

// Remove x-powered-by header
app.disable('x-powered-by');

// server static files
app.use('/static', express.static('app'));

// Load main web page
app.get('/', function (req, res) {
  res.sendFile(path.resolve('app/index.html'));
});

var allClients = [];

// When a socket connection is created
io.on('connection', function (socket) {
  allClients.push(socket);
  redis_client.incr('connected_users');
  socket.on('disconnect', function() {
     logger.v('Got disconnect!');
     var i = allClients.indexOf(socket);
     allClients.splice(i, 1);
     redis_client.decr('connected_users');
  });
  socket.on('error', function(){
    logger.error('Got errored!');
    redis_client.decr('connected_users');
  })
});

// Function to get events from GitHub API
function fetchDataFromGithub(){
  var options = {
    url: 'https://api.github.com/events',
    headers: {
      'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36',
      'Authorization': 'token ' + process.env.GITHUB_OAUTH_KEY
    }
  };
  request(options, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      var data = JSON.parse(body);
      var stripedData = stripData(data);  // Keep only useful keys
      allClients.forEach(function(socket){
        if(socket != null && socket.connected == true){
            redis_client.get('connected_users', function(err, count) {
                if(!err && count != null){
                    socket.volatile.json.emit('github', {data: stripedData, connected_users: count});
                }else{
                  logger.error(err.message);
                }
            });
        }
      });

    }else{
      logger.error("GitHub status code: " + response.statusCode);
    }
  })
  setTimeout(fetchDataFromGithub, 2000);
}
setTimeout(fetchDataFromGithub, 2000);

function stripData(data){
  var stripedData = [];
  var pushEventCounter = 0;
  var IssueCommentEventCounter = 0;
  var IssuesEventCounter = 0;
  data.forEach(function(data){
    if(data.type == 'PushEvent'){
      if(pushEventCounter > 3) return;
      if(data.payload.size != 0){
        stripedData.push({
          'id': data.id,
          'type': data.type,
          'user': data.actor.display_login,
          'user_avatar': data.actor.avatar_url + 'v=3&s=64',
          'repo_id': data.repo.id,
          'repo_name': data.repo.name,
          'payload_size': data.payload.size,
          'message': data.payload.commits[0].message,
          'created': data.created_at
        });
        pushEventCounter++;
      }
    }else if(data.type == 'IssueCommentEvent'){
      stripedData.push({
        'id': data.id,
        'type': data.type,
        'user': data.actor.display_login,
        'user_avatar': data.actor.avatar_url + 'v=3&s=64',
        'repo_id': data.repo.id,
        'repo_name': data.repo.name,
        'payload_size': 0,
        'message': data.body,
        'created': data.created_at
      });
    }else if(data.type == 'PullRequestEvent'){
      if (data.payload.pull_request.merged) data.payload.action = 'merged';
      stripedData.push({
        'id': data.id,
        'type': data.type,
        'user': data.actor.display_login,
        'user_avatar': data.actor.avatar_url + 'v=3&s=64',
        'repo_id': data.repo.id,
        'repo_name': data.repo.name,
        'action': data.payload.action,  // opened, reopened, closed, merged
        'message': data.payload.pull_request.title,
        'created': data.created_at
      });
    }else if(data.type == 'IssuesEvent'){
      stripedData.push({
        'id': data.id,
        'type': data.type,
        'user': data.actor.display_login,
        'user_avatar': data.actor.avatar_url + 'v=3&s=64',
        'repo_id': data.repo.id,
        'repo_name': data.repo.name,
        'action': data.payload.action,  // opened, reopened, closed
        'message': data.payload.issue.title,
        'created': data.created_at
      });
    }
  });
  return stripedData;
}
<|endoftext|>


<commit_before>const parser = require('ua-parser-js');
const { uniqueNamesGenerator, animals, colors } = require('unique-names-generator');

class SnapdropServer {

    constructor(port) {
        const WebSocket = require('ws');
        this._wss = new WebSocket.Server({ port: port });
        this._wss.on('connection', (socket, request) => this._onConnection(new Peer(socket, request)));
        this._wss.on('headers', (headers, response) => this._onHeaders(headers, response));

        this._rooms = {};

        console.log('Snapdrop is running on port', port);
    }

    _onConnection(peer) {
        this._joinRoom(peer);
        peer.socket.on('message', message => this._onMessage(peer, message));
        this._keepAlive(peer);

        // send displayName
        this._send(peer, { type: 'displayName', message: peer.name.displayName });
    }

    _onHeaders(headers, response) {
        if (response.headers.cookie && response.headers.cookie.indexOf('peerid=') > -1) return;
        response.peerId = Peer.uuid();
        headers.push('Set-Cookie: peerid=' + response.peerId + "; SameSite=Strict; Secure");
    }

    _onMessage(sender, message) {
        // Try to parse message
        try {
            message = JSON.parse(message);
        } catch (e) {
            return; // TODO: handle malformed JSON
        }

        switch (message.type) {
            case 'disconnect':
                this._leaveRoom(sender);
                break;
            case 'pong':
                sender.lastBeat = Date.now();
                break;
        }

        // relay message to recipient
        if (message.to && this._rooms[sender.ip]) {
            const recipientId = message.to; // TODO: sanitize
            const recipient = this._rooms[sender.ip][recipientId];
            delete message.to;
            // add sender id
            message.sender = sender.id;
            this._send(recipient, message);
            return;
        }
    }

    _joinRoom(peer) {
        // if room doesn't exist, create it
        if (!this._rooms[peer.ip]) {
            this._rooms[peer.ip] = {};
        }

        // notify all other peers
        for (const otherPeerId in this._rooms[peer.ip]) {
            const otherPeer = this._rooms[peer.ip][otherPeerId];
            this._send(otherPeer, {
                type: 'peer-joined',
                peer: peer.getInfo()
            });
        }

        // notify peer about the other peers
        const otherPeers = [];
        for (const otherPeerId in this._rooms[peer.ip]) {
            otherPeers.push(this._rooms[peer.ip][otherPeerId].getInfo());
        }

        this._send(peer, {
            type: 'peers',
            peers: otherPeers
        });

        // add peer to room
        this._rooms[peer.ip][peer.id] = peer;
    }

    _leaveRoom(peer) {
        if (!this._rooms[peer.ip] || !this._rooms[peer.ip][peer.id]) return;
        this._cancelKeepAlive(this._rooms[peer.ip][peer.id]);

        // delete the peer
        delete this._rooms[peer.ip][peer.id];

        peer.socket.terminate();
        //if room is empty, delete the room
        if (!Object.keys(this._rooms[peer.ip]).length) {
            delete this._rooms[peer.ip];
        } else {
            // notify all other peers
            for (const otherPeerId in this._rooms[peer.ip]) {
                const otherPeer = this._rooms[peer.ip][otherPeerId];
                this._send(otherPeer, { type: 'peer-left', peerId: peer.id });
            }
        }
    }

    _send(peer, message) {
        if (!peer) return;
        if (this._wss.readyState !== this._wss.OPEN) return;
        message = JSON.stringify(message);
        peer.socket.send(message, error => '');
    }

    _keepAlive(peer) {
        this._cancelKeepAlive(peer);
        var timeout = 30000;
        if (!peer.lastBeat) {
            peer.lastBeat = Date.now();
        }
        if (Date.now() - peer.lastBeat > 2 * timeout) {
            this._leaveRoom(peer);
            return;
        }

        this._send(peer, { type: 'ping' });

        peer.timerId = setTimeout(() => this._keepAlive(peer), timeout);
    }

    _cancelKeepAlive(peer) {
        if (peer && peer.timerId) {
            clearTimeout(peer.timerId);
        }
    }
}

class Peer {

    constructor(socket, request) {
        // set socket
        this.socket = socket;

        // set remote ip
        this._setIP(request);

        // set peer id
        this._setPeerId(request)
        // is WebRTC supported ?
        this.rtcSupported = request.url.indexOf('webrtc') > -1;
        // set name
        this._setName(request);
        // for keepalive
        this.timerId = 0;
        this.lastBeat = Date.now();
    }

    _setIP(request) {
        if (request.headers['x-forwarded-for']) {
            this.ip = request.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
        } else {
            this.ip = request.connection.remoteAddress;
        }
        // IPv4 and IPv6 use different values to refer to localhost
        if (this.ip == '::1' || this.ip == '::ffff:127.0.0.1') {
            this.ip = '127.0.0.1';
        }
    }

    _setPeerId(request) {
        if (request.peerId) {
            this.id = request.peerId;
        } else {
            this.id = request.headers.cookie.replace('peerid=', '');
        }
    }

    toString() {
        return `<Peer id=${this.id} ip=${this.ip} rtcSupported=${this.rtcSupported}>`
    }

    _setName(req) {
        var ua = parser(req.headers['user-agent']);
        this.name = {
            model: ua.device.model,
            os: ua.os.name,
            browser: ua.browser.name,
            type: ua.device.type,
            displayName: uniqueNamesGenerator({ length: 2, separator: ' ', dictionaries: [colors, animals], style: 'capital' })
        };
    }

    getInfo() {
        return {
            id: this.id,
            name: this.name,
            rtcSupported: this.rtcSupported
        }
    }

    // return uuid of form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
    static uuid() {
        let uuid = '',
            ii;
        for (ii = 0; ii < 32; ii += 1) {
            switch (ii) {
                case 8:
                case 20:
                    uuid += '-';
                    uuid += (Math.random() * 16 | 0).toString(16);
                    break;
                case 12:
                    uuid += '-';
                    uuid += '4';
                    break;
                case 16:
                    uuid += '-';
                    uuid += (Math.random() * 4 | 8).toString(16);
                    break;
                default:
                    uuid += (Math.random() * 16 | 0).toString(16);
            }
        }
        return uuid;
    };
}

const server = new SnapdropServer(process.env.PORT || 3000);
<commit_msg>Add support for SIGINT and SIGTERM handling<commit_after>var process = require('process')
// Handle SIGINT
process.on('SIGINT', () => {
  console.info("SIGINT Received, exiting...")
  process.exit(0)
})

// Handle SIGTERM
process.on('SIGTERM', () => {
  console.info("SIGTERM Received, exiting...")
  process.exit(0)
})

const parser = require('ua-parser-js');
const { uniqueNamesGenerator, animals, colors } = require('unique-names-generator');

class SnapdropServer {

    constructor(port) {
        const WebSocket = require('ws');
        this._wss = new WebSocket.Server({ port: port });
        this._wss.on('connection', (socket, request) => this._onConnection(new Peer(socket, request)));
        this._wss.on('headers', (headers, response) => this._onHeaders(headers, response));

        this._rooms = {};

        console.log('Snapdrop is running on port', port);
    }

    _onConnection(peer) {
        this._joinRoom(peer);
        peer.socket.on('message', message => this._onMessage(peer, message));
        this._keepAlive(peer);

        // send displayName
        this._send(peer, { type: 'displayName', message: peer.name.displayName });
    }

    _onHeaders(headers, response) {
        if (response.headers.cookie && response.headers.cookie.indexOf('peerid=') > -1) return;
        response.peerId = Peer.uuid();
        headers.push('Set-Cookie: peerid=' + response.peerId + "; SameSite=Strict; Secure");
    }

    _onMessage(sender, message) {
        // Try to parse message
        try {
            message = JSON.parse(message);
        } catch (e) {
            return; // TODO: handle malformed JSON
        }

        switch (message.type) {
            case 'disconnect':
                this._leaveRoom(sender);
                break;
            case 'pong':
                sender.lastBeat = Date.now();
                break;
        }

        // relay message to recipient
        if (message.to && this._rooms[sender.ip]) {
            const recipientId = message.to; // TODO: sanitize
            const recipient = this._rooms[sender.ip][recipientId];
            delete message.to;
            // add sender id
            message.sender = sender.id;
            this._send(recipient, message);
            return;
        }
    }

    _joinRoom(peer) {
        // if room doesn't exist, create it
        if (!this._rooms[peer.ip]) {
            this._rooms[peer.ip] = {};
        }

        // notify all other peers
        for (const otherPeerId in this._rooms[peer.ip]) {
            const otherPeer = this._rooms[peer.ip][otherPeerId];
            this._send(otherPeer, {
                type: 'peer-joined',
                peer: peer.getInfo()
            });
        }

        // notify peer about the other peers
        const otherPeers = [];
        for (const otherPeerId in this._rooms[peer.ip]) {
            otherPeers.push(this._rooms[peer.ip][otherPeerId].getInfo());
        }

        this._send(peer, {
            type: 'peers',
            peers: otherPeers
        });

        // add peer to room
        this._rooms[peer.ip][peer.id] = peer;
    }

    _leaveRoom(peer) {
        if (!this._rooms[peer.ip] || !this._rooms[peer.ip][peer.id]) return;
        this._cancelKeepAlive(this._rooms[peer.ip][peer.id]);

        // delete the peer
        delete this._rooms[peer.ip][peer.id];

        peer.socket.terminate();
        //if room is empty, delete the room
        if (!Object.keys(this._rooms[peer.ip]).length) {
            delete this._rooms[peer.ip];
        } else {
            // notify all other peers
            for (const otherPeerId in this._rooms[peer.ip]) {
                const otherPeer = this._rooms[peer.ip][otherPeerId];
                this._send(otherPeer, { type: 'peer-left', peerId: peer.id });
            }
        }
    }

    _send(peer, message) {
        if (!peer) return;
        if (this._wss.readyState !== this._wss.OPEN) return;
        message = JSON.stringify(message);
        peer.socket.send(message, error => '');
    }

    _keepAlive(peer) {
        this._cancelKeepAlive(peer);
        var timeout = 30000;
        if (!peer.lastBeat) {
            peer.lastBeat = Date.now();
        }
        if (Date.now() - peer.lastBeat > 2 * timeout) {
            this._leaveRoom(peer);
            return;
        }

        this._send(peer, { type: 'ping' });

        peer.timerId = setTimeout(() => this._keepAlive(peer), timeout);
    }

    _cancelKeepAlive(peer) {
        if (peer && peer.timerId) {
            clearTimeout(peer.timerId);
        }
    }
}

class Peer {

    constructor(socket, request) {
        // set socket
        this.socket = socket;

        // set remote ip
        this._setIP(request);

        // set peer id
        this._setPeerId(request)
        // is WebRTC supported ?
        this.rtcSupported = request.url.indexOf('webrtc') > -1;
        // set name
        this._setName(request);
        // for keepalive
        this.timerId = 0;
        this.lastBeat = Date.now();
    }

    _setIP(request) {
        if (request.headers['x-forwarded-for']) {
            this.ip = request.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
        } else {
            this.ip = request.connection.remoteAddress;
        }
        // IPv4 and IPv6 use different values to refer to localhost
        if (this.ip == '::1' || this.ip == '::ffff:127.0.0.1') {
            this.ip = '127.0.0.1';
        }
    }

    _setPeerId(request) {
        if (request.peerId) {
            this.id = request.peerId;
        } else {
            this.id = request.headers.cookie.replace('peerid=', '');
        }
    }

    toString() {
        return `<Peer id=${this.id} ip=${this.ip} rtcSupported=${this.rtcSupported}>`
    }

    _setName(req) {
        var ua = parser(req.headers['user-agent']);
        this.name = {
            model: ua.device.model,
            os: ua.os.name,
            browser: ua.browser.name,
            type: ua.device.type,
            displayName: uniqueNamesGenerator({ length: 2, separator: ' ', dictionaries: [colors, animals], style: 'capital' })
        };
    }

    getInfo() {
        return {
            id: this.id,
            name: this.name,
            rtcSupported: this.rtcSupported
        }
    }

    // return uuid of form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
    static uuid() {
        let uuid = '',
            ii;
        for (ii = 0; ii < 32; ii += 1) {
            switch (ii) {
                case 8:
                case 20:
                    uuid += '-';
                    uuid += (Math.random() * 16 | 0).toString(16);
                    break;
                case 12:
                    uuid += '-';
                    uuid += '4';
                    break;
                case 16:
                    uuid += '-';
                    uuid += (Math.random() * 4 | 8).toString(16);
                    break;
                default:
                    uuid += (Math.random() * 16 | 0).toString(16);
            }
        }
        return uuid;
    };
}

const server = new SnapdropServer(process.env.PORT || 3000);
<|endoftext|>


<commit_before>const { querySelectorDeep } = require('query-selector-shadow-dom');

exports.getDocument = () => document.getElementById('app').contentDocument;

exports.querySelectorDeep = querySelectorDeep;

exports.waitFor = seconds => new Promise((resolve) => {
  setTimeout(resolve, seconds);
});

exports.waitForElement = (selector, ensureNoError = true, container = null) => new Promise(
  (resolve, reject) => {
    const parentElement = container || exports.getDocument();
    if (ensureNoError) {
      try {
        exports.assertNoError();
      } catch (e) {
        reject(e);
        return;
      }
    }
    const element = querySelectorDeep(selector, parentElement);
    if (element) {
      resolve(element);
      return;
    }
    exports.waitFor(100)
      .then(() => exports.waitForElement(selector, ensureNoError, parentElement))
      .then(resolve, reject);
  },
);

exports.assertNoError = () => {
  const alertDialog = querySelectorDeep('noflo-ui noflo-alert', exports.getDocument());
  if (!alertDialog.classList.contains('show')) {
    return;
  }
  if (!alertDialog.classList.contains('error')) {
    return;
  }
  const alertContents = querySelectorDeep('noflo-ui noflo-alert slot span', exports.getDocument());
  throw new Error(alertContents.innerHTML);
};
<commit_msg>No need to expose this, users can just require the lib<commit_after>const { querySelectorDeep } = require('query-selector-shadow-dom');

exports.getDocument = () => document.getElementById('app').contentDocument;

exports.waitFor = seconds => new Promise((resolve) => {
  setTimeout(resolve, seconds);
});

exports.waitForElement = (selector, ensureNoError = true, container = null) => new Promise(
  (resolve, reject) => {
    const parentElement = container || exports.getDocument();
    if (ensureNoError) {
      try {
        exports.assertNoError();
      } catch (e) {
        reject(e);
        return;
      }
    }
    const element = querySelectorDeep(selector, parentElement);
    if (element) {
      resolve(element);
      return;
    }
    exports.waitFor(100)
      .then(() => exports.waitForElement(selector, ensureNoError, parentElement))
      .then(resolve, reject);
  },
);

exports.assertNoError = () => {
  const alertDialog = querySelectorDeep('noflo-ui noflo-alert', exports.getDocument());
  if (!alertDialog.classList.contains('show')) {
    return;
  }
  if (!alertDialog.classList.contains('error')) {
    return;
  }
  const alertContents = querySelectorDeep('noflo-ui noflo-alert slot span', exports.getDocument());
  throw new Error(alertContents.innerHTML);
};
<|endoftext|>


<commit_before>/* eslint consistent-return:0 */

const express = require('express');
const logger = require('./logger');

const argv = require('./argv');
const port = require('./port');
const setup = require('./middlewares/frontendMiddleware');
const isDev = process.env.NODE_ENV !== 'production';
const ngrok = (isDev && process.env.ENABLE_TUNNEL) || argv.tunnel ? require('ngrok') : false;
const { resolve } = require('path');
const app = express();

// If you need a backend, e.g. an API, add your custom backend-specific middleware here
// app.use('/api', myApi);

// In production we need to pass these values in instead of relying on webpack
setup(app, {
  outputPath: resolve(process.cwd(), 'build'),
  publicPath: '/',
});

// get the intended host and port number, use localhost and port 3000 if not provided
const customHost = argv.host || process.env.HOST;
const host = customHost || null; // Let http.Server use its default IPv6/4 host
const prettyHost = customHost || 'localhost';

// Start your app.
app.listen(port, host, (err) => {
  if (err) {
    return logger.error(err.message);
  }

  // Connect to ngrok in dev mode
  if (ngrok) {
    ngrok.connect(port, (innerErr, url) => {
      if (innerErr) {
        return logger.error(innerErr);
      }

      logger.appStarted(port, prettyHost, url);
    });
  } else {
    logger.appStarted(port, prettyHost);
  }
});
<commit_msg>fix: newest ngrok uses async/await<commit_after>/* eslint consistent-return:0 */

const express = require('express');
const logger = require('./logger');

const argv = require('./argv');
const port = require('./port');
const setup = require('./middlewares/frontendMiddleware');
const isDev = process.env.NODE_ENV !== 'production';
const ngrok = (isDev && process.env.ENABLE_TUNNEL) || argv.tunnel ? require('ngrok') : false;
const { resolve } = require('path');
const app = express();

// If you need a backend, e.g. an API, add your custom backend-specific middleware here
// app.use('/api', myApi);

// In production we need to pass these values in instead of relying on webpack
setup(app, {
  outputPath: resolve(process.cwd(), 'build'),
  publicPath: '/',
});

// get the intended host and port number, use localhost and port 3000 if not provided
const customHost = argv.host || process.env.HOST;
const host = customHost || null; // Let http.Server use its default IPv6/4 host
const prettyHost = customHost || 'localhost';

// Start your app.
app.listen(port, host, (err) => {
  if (err) {
    return logger.error(err.message);
  }

  // Connect to ngrok in dev mode
  if (ngrok) {
    (async () => {
      let url;
      try {
        url = await ngrok.connect(port);
      } catch (ngrokErr) {
        logger.error(ngrokErr);
      }
      logger.appStarted(port, prettyHost, url);
    })();
  } else {
    logger.appStarted(port, prettyHost);
  }
});
<|endoftext|>


<commit_before>export { Bounds, ScaleTranslate };

/**
 * Describe the [ lower, upper ] bounds of a floating-point value
 *
 * @member {number} lower
 *   The lower bound of the range.
 *
 * @member {number} upper
 *   The upper bound of the range.
 *
 * @member {number} difference
 *   The distance between lower and upper.
 */
class Bounds {

  constructor( lower, upper ) {
    this.lower = Math.min( lower, upper ); // Confirm ( lower <= upper ).
    this.upper = Math.max( lower, upper );
  }

  get difference() {
    let difference = this.upper - this.lower;
    return difference;
  }

  /**
   * @return {Bounds}
   *   Return newly created object which is a copy of this Bounds.
   */
  clone() {
    return new Bounds( this.lower, this.upper );
  }

  /**
   * @param {Bounds} aBounds
   *   Set this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which copies aBounds.
   */
  set_Bounds( aBounds ) {
    this.lower = aBounds.lower;
    this.upper = aBounds.upper;
    return this;
  }

  /**
   * @param {number} aLower
   *   Set this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Set this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is [ aLower, aUpper ].
   */
  set_LowerUpper( aLower, aUpper ) {
    this.lower = aLower;
    this.upper = aUpper;
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Add this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which is added by aBounds.
   */
  set_add_Bounds( aBounds ) {
    this.lower += aBounds.lower;
    this.upper += aBounds.upper;
    return this;
  }

  /**
   * @param {number} aLower
   *   Add this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Add this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is added by Bounds [ aLower, aUpper ].
   */
  set_add_LowerUpper( aLower, aUpper ) {
    this.lower += aLower;
    this.upper += aUpper;
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Multiply this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which is multiplied by aBounds.
   */
  set_multiply_Bounds( aBounds ) {
    this.lower *= aBounds.lower;
    this.upper *= aBounds.upper;
    return this;
  }

  /**
   * @param {number} aLower
   *   Multiply this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Multiply this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is multiplied by Bounds [ aLower, aUpper ].
   */
  set_multiply_LowerUpper( aLower, aUpper ) {
    this.lower *= aLower;
    this.upper *= aUpper;
    return this;
  }

  /**
   * @param {number} N
   *   The multiplier of this.lower and this.upper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is the same as this.set_multiply_LowerUpper( N, N ) or repeating N times this.set_add_Bounds( this ).
   */
  set_multiply_N( N ) {
    this.lower *= N;
    this.upper *= N;
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   The first multiplier (a Bounds).
   *
   * @param {number} N
   *   The second multiplier (a number).
   *
   * @return {Bounds}
   *   Return this (modified) object which is the sumed Bounds of repeating N times of multiplying this by aBounds.
   */
  set_multiply_Bounds_multiply_N( aBounds, N ) {
    this.lower = ( this.lower * aBounds.lower ) * N;
    this.upper = ( this.upper * aBounds.upper ) * N;
    return this;

    // The same as:
    //return this.set_multiply_Bounds( aBounds ).set_multiply_N( N );
  }

  /**
   * Confirm:
   *   - Every element is not NaN. (If it is, become 0.)
   *   - Every element is between [ lower, upper ].
   *
   * @param {Float32Array} source
   *   The source Float32Array.
   *
   * @return {Float32Array}
   *   Return a copy of source. Every element (float32):
   *     - If ( Number.isNaN( element ) == true ), let it become 0.
   *     - Otherwise, Math.max( lower, Math.min( element, upper ) ).
   */
  Float32Array_RestrictedClone( sourceArray ) {
    let resultArray = new Float32Array( sourceArray.length );
    for ( let i = 0; i < sourceArray.length; ++i ) {
      let element = sourceArray[ i ];
      if ( !Number.isNaN( element ) ) {
        resultArray[ i ] = Math.max( this.lower, Math.min( element, this.upper ) );
      } // If NaN, let it become 0. (Just do nothing, because Float32Array is initialized to zero by default.)
    }
    return resultArray;
  }

}

/**
 * Describe a scale (i.e. multiplier) value, and then a translate (i.e. offset; bias) value after the scale.
 *
 */
class ScaleTranslate {

  constructor( scale, translate ) {
    this.scale = scale;
    this.translate = translate;
  }

  /**
   * Set this.scale and this.translate for mapping values from source bounds to target bounds.
   *
   * @param {Bounds} source
   *   The range of the source value.
   *
   * @param {Bounds} target
   *   The range of the target value.
   */
  setBy_FromTo( source, target ) {
    // Suppose x is a value inside the source range. y is the corresponding value inside the target range.
    //
    //   y = target.lower + ( target.difference * ( x - source.lower ) / source.difference )
    //     = target.lower + ( ( ( target.difference * x ) - ( target.difference * source.lower ) ) / source.difference )
    //     = target.lower + ( ( ( target.difference * x ) / source.difference ) - ( ( target.difference * source.lower ) / source.difference ) )
    //     = target.lower + ( ( ( target.difference / source.difference ) * x ) - ( ( target.difference * source.lower ) / source.difference ) )
    //     = target.lower + ( ( target.difference / source.difference ) * x ) - ( ( target.difference * source.lower ) / source.difference )
    //     = ( ( target.difference / source.difference ) * x ) + ( target.lower - ( ( target.difference * source.lower ) / source.difference ) )
    //     = ( scale * x ) + translate
    //
    // Got:
    //   scale = ( target.difference / source.difference )
    //   translate = ( target.lower - ( ( target.difference * source.lower ) / source.difference ) )
    //             = ( target.lower - ( ( target.difference / source.difference ) * source.lower ) )
    //             = ( target.lower - ( scale * source.lower ) )
    //
    // For example:
    //   - from [ 2, 12 ] to [ -3, -1 ]
    //   - scale  = 0.2
    //   - translate = -3.4
    //
    this.scale = ( target.difference / source.difference );
    this.translate = ( target.lower - ( this.scale * source.lower ) );
  }

  /**
   * @param {Bounds} source
   *   The range of the source value.
   *
   * @param {Bounds} target
   *   The range of the target value.
   *
   * @return {ScaleTranslate}
   *   Create and return { scale, translate } for mapping values from sourceMinMax to targetMinMax.
   */
  static createBy_FromTo( source, target ) {
    // (Please see ScaleTranslate.setBy_FromTo().)
    let scale = ( target.difference / source.difference );
    let translate = ( target.lower - ( scale * source.lower ) );
    let result = new ScaleTranslate( scale, translate );
    return result;
  }

}

<commit_msg>Update FloatValue.js<commit_after>export { Bounds, ScaleTranslate };

/**
 * Describe the [ lower, upper ] bounds of a floating-point value
 *
 * @member {number} lower
 *   The lower bound of the range.
 *
 * @member {number} upper
 *   The upper bound of the range.
 *
 * @member {number} difference
 *   The distance between lower and upper.
 */
class Bounds {

  constructor( lower, upper ) {
    this.lower = Math.min( lower, upper ); // Confirm ( lower <= upper ).
    this.upper = Math.max( lower, upper );
  }

  get difference() {
    let difference = this.upper - this.lower;
    return difference;
  }

  /**
   * @return {Bounds}
   *   Return newly created object which is a copy of this Bounds.
   */
  clone() {
    return new Bounds( this.lower, this.upper );
  }

  /**
   * @param {Bounds} aBounds
   *   Set this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which copies aBounds.
   */
  set_Bounds( aBounds ) {
    this.lower = aBounds.lower;
    this.upper = aBounds.upper;
    return this;
  }

  /**
   * @param {number} aLower
   *   Set this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Set this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is [ aLower, aUpper ].
   */
  set_LowerUpper( aLower, aUpper ) {
    this.lower = aLower;
    this.upper = aUpper;
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Add this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which is added by aBounds.
   */
  add_Bounds( aBounds ) {
    this.lower += aBounds.lower;
    this.upper += aBounds.upper;
    return this;
  }

  /**
   * @param {number} aLower
   *   Add this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Add this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is added by Bounds [ aLower, aUpper ].
   */
  add_LowerUpper( aLower, aUpper ) {
    this.lower += aLower;
    this.upper += aUpper;
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Multiply this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which is multiplied by aBounds.
   */
  multiply_Bounds( aBounds ) {
    this.lower *= aBounds.lower;
    this.upper *= aBounds.upper;
    return this;
  }

  /**
   * @param {number} aLower
   *   Multiply this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Multiply this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is multiplied by Bounds [ aLower, aUpper ].
   */
  multiply_LowerUpper( aLower, aUpper ) {
    this.lower *= aLower;
    this.upper *= aUpper;
    return this;
  }

  /**
   * @param {number} N
   *   The multiplier of this.lower and this.upper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is the same as this.multiply_LowerUpper( N, N ) or repeating N times this.add_Bounds( this ).
   */
  multiply_N( N ) {
    this.lower *= N;
    this.upper *= N;
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   The first multiplier (a Bounds).
   *
   * @param {number} N
   *   The second multiplier (a number).
   *
   * @return {Bounds}
   *   Return this (modified) object which is the sumed Bounds of repeating N times of multiplying this by aBounds.
   */
  multiply_Bounds_multiply_N( aBounds, N ) {
    this.lower = ( this.lower * aBounds.lower ) * N;
    this.upper = ( this.upper * aBounds.upper ) * N;
    return this;

    // The same as:
    //return this.multiply_Bounds( aBounds ).multiply_N( N );
  }

  /**
   * Confirm:
   *   - Every element is not NaN. (If it is, become 0.)
   *   - Every element is between [ lower, upper ].
   *
   * @param {Float32Array} source
   *   The source Float32Array.
   *
   * @return {Float32Array}
   *   Return a copy of source. Every element (float32):
   *     - If ( Number.isNaN( element ) == true ), let it become 0.
   *     - Otherwise, Math.max( lower, Math.min( element, upper ) ).
   */
  Float32Array_RestrictedClone( sourceArray ) {
    let resultArray = new Float32Array( sourceArray.length );
    for ( let i = 0; i < sourceArray.length; ++i ) {
      let element = sourceArray[ i ];
      if ( !Number.isNaN( element ) ) {
        resultArray[ i ] = Math.max( this.lower, Math.min( element, this.upper ) );
      } // If NaN, let it become 0. (Just do nothing, because Float32Array is initialized to zero by default.)
    }
    return resultArray;
  }

}

/**
 * Describe a scale (i.e. multiplier) value, and then a translate (i.e. offset; bias) value after the scale.
 *
 */
class ScaleTranslate {

  constructor( scale, translate ) {
    this.scale = scale;
    this.translate = translate;
  }

  /**
   * Set this.scale and this.translate for mapping values from source bounds to target bounds.
   *
   * @param {Bounds} source
   *   The range of the source value.
   *
   * @param {Bounds} target
   *   The range of the target value.
   */
  setBy_FromTo( source, target ) {
    // Suppose x is a value inside the source range. y is the corresponding value inside the target range.
    //
    //   y = target.lower + ( target.difference * ( x - source.lower ) / source.difference )
    //     = target.lower + ( ( ( target.difference * x ) - ( target.difference * source.lower ) ) / source.difference )
    //     = target.lower + ( ( ( target.difference * x ) / source.difference ) - ( ( target.difference * source.lower ) / source.difference ) )
    //     = target.lower + ( ( ( target.difference / source.difference ) * x ) - ( ( target.difference * source.lower ) / source.difference ) )
    //     = target.lower + ( ( target.difference / source.difference ) * x ) - ( ( target.difference * source.lower ) / source.difference )
    //     = ( ( target.difference / source.difference ) * x ) + ( target.lower - ( ( target.difference * source.lower ) / source.difference ) )
    //     = ( scale * x ) + translate
    //
    // Got:
    //   scale = ( target.difference / source.difference )
    //   translate = ( target.lower - ( ( target.difference * source.lower ) / source.difference ) )
    //             = ( target.lower - ( ( target.difference / source.difference ) * source.lower ) )
    //             = ( target.lower - ( scale * source.lower ) )
    //
    // For example:
    //   - from [ 2, 12 ] to [ -3, -1 ]
    //   - scale  = 0.2
    //   - translate = -3.4
    //
    this.scale = ( target.difference / source.difference );
    this.translate = ( target.lower - ( this.scale * source.lower ) );
  }

  /**
   * @param {Bounds} source
   *   The range of the source value.
   *
   * @param {Bounds} target
   *   The range of the target value.
   *
   * @return {ScaleTranslate}
   *   Create and return { scale, translate } for mapping values from sourceMinMax to targetMinMax.
   */
  static createBy_FromTo( source, target ) {
    // (Please see ScaleTranslate.setBy_FromTo().)
    let scale = ( target.difference / source.difference );
    let translate = ( target.lower - ( scale * source.lower ) );
    let result = new ScaleTranslate( scale, translate );
    return result;
  }

}

<|endoftext|>


<commit_before>var page = require('webpage').create(),
    async = require('../node_modules/async/lib/async.js'),
    downloadLink;

// Run a check function every 100 ms
function waitFor(checkFn, cb) {
  if (checkFn()) {
    cb();
  } else {
    setTimeout(function waitForFn () {
      waitFor(checkFn, cb);
    }, 100);
  }
}

// In series
async.series([
  function openIcoMoon (cb) {
    // Open IcoMoon
    page.open('http://icomoon.io/app', cb);
  },
  function waitForLoading (cb) {
    // Wait for loading to complete
    waitFor(function () {
      return page.evaluate(function () {
        return !document.getElementById('loading');
      });
    }, cb);
  },
  function uploadImages (cb) {
    // Submit many images (need a file path for all our images)
    var fs = require('fs'),
        filesPath = phantom.args[3],
        filesJSON = fs.read(filesPath),
        files = JSON.parse(filesJSON);

    // TODO: We might need to submit this many times per file
    page.uploadFile('#files', files);

    // Continue
    cb();
  },
  function exportFont (cb) {
    // Export our font
    page.evaluate(function () {
      // Select our images
      $('#imported').find('.checkbox-icon').click();

      // "Click" the 'Next' link
      window.location.hash = '#font';

      // Click the download link
      $('#saveFont').click();
    });

    // Continue
    cb();
  },
  function waitForRedirect (cb) {
    // Wait for a redirect request
    page.onNavigationRequested = function (url) {
      downloadLink = url;
    };
    waitFor(function () {
      return downloadLink;
    }, cb);
  }
], function output (err) {
  // DEV: Take a screenshot for progress
  page.render('app.png');

  // If there was an error, log it
  if (err) {
    console.error(err);
    phantom.exit(1);
  } else {
  // Otherwise, output the download link for the next module
    console.log(downloadLink);
    phantom.exit(0);
  }
});
<commit_msg>Repaired open command<commit_after>var page = require('webpage').create(),
    async = require('../node_modules/async/lib/async.js'),
    downloadLink;

console.log('hey');
// Run a check function every 100 ms
function waitFor(checkFn, cb) {
  console.log('hmmm');
  if (checkFn()) {
    console.log('cb');
    cb();
  } else {
      console.log('wait');
    setTimeout(function waitForFn () {
      console.log('wait2');
      waitFor(checkFn, cb);
    }, 100);
  }
}

// In series
async.series([
  function openIcoMoon (cb) {
    // Open IcoMoon
    page.open('http://icomoon.io/app', function (status) {
      cb();
    });
    console.log('hey');
  },
  function waitForLoading (cb) {
    // Wait for loading to complete
    waitFor(function () {
      console.log('wwww');
      return page.evaluate(function () {
        return !document.getElementById('loading');
      });
    }, cb);
    console.log('dddd');
  },
  function uploadImages (cb) {
    // Submit many images (need a file path for all our images)
    console.log('hey');
    console.log(phantom.args[3]);
    var fs = require('fs'),
        filesPath = phantom.args[3],
        filesJSON = fs.read(filesPath),
        files = JSON.parse(filesJSON);

    // TODO: We might need to submit this many times per file
    page.uploadFile('#files', files);

    // Continue
    cb();
  },
  function exportFont (cb) {
    console.log('hey');
    // Export our font
    page.evaluate(function () {
      // Select our images
      $('#imported').find('.checkbox-icon').click();

      // "Click" the 'Next' link
      window.location.hash = '#font';

      // Click the download link
      $('#saveFont').click();
    });

    // Continue
    cb();
  },
  function waitForRedirect (cb) {
    console.log('hey');
    console.log('hey');
    // Wait for a redirect request
    page.onNavigationRequested = function (url) {
      downloadLink = url;
    };
    waitFor(function () {
      return downloadLink;
    }, cb);
  }
], function output (err) {
  // DEV: Take a screenshot for progress
  page.render('app.png');

  // If there was an error, log it
  if (err) {
    console.error(err);
    phantom.exit(1);
  } else {
  // Otherwise, output the download link for the next module
    console.log(downloadLink);
    phantom.exit(0);
  }
});<|endoftext|>


<commit_before>/**
 * @license
 * Copyright (c) 2018 Google Inc. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt
 * Code distributed by Google as part of this project is also
 * subject to an additional IP rights grant found at
 * http://polymer.github.io/PATENTS.txt
 */
import {assert} from '../platform/assert-web.js';
import {Affordance} from './affordance.js';

export class SuggestionComposer {
  constructor(slotComposer) {
    assert(slotComposer);
    this._affordance = Affordance.forName(slotComposer.affordance);
    // TODO(mmandlis): find a cleaner way to fetch suggestions context.
    this._context = slotComposer._contextSlots.find(context => context.name == 'suggestions').container;
    assert(this._context);

    this._suggestions = [];
    this._suggestionsQueue = [];
    this._updateComplete = null;
  }

  async setSuggestions(suggestions) {
    this._suggestionsQueue.push(suggestions);
    Promise.resolve().then(async () => {
      if (this._updateComplete) {
        await this._updateComplete;
      }
      if (this._suggestionsQueue.length > 0) {
        this._suggestions = this._suggestionsQueue.pop();
        this._suggestionsQueue = [];
        this._updateComplete = this._updateSuggestions(this._suggestions);
      }
    });
  }

  async _updateSuggestions(suggestions) {
    this._affordance.contextClass.clear(this._context);
    return Promise.all(suggestions.map(async suggestion => {
      let suggestionContent =
        await suggestion.description.getRecipeSuggestion(this._affordance.descriptionFormatter);
      assert(suggestionContent, 'No suggestion content available');
      this._affordance.contextClass.createContext(
          this.createSuggestionElement(this._context, suggestion), suggestionContent);
    }));
  }

  createSuggestionElement(container, plan) {
    let suggest = Object.assign(document.createElement('suggestion-element'), {plan});
    // TODO(sjmiles): LIFO is weird, iterate top-down elsewhere?
    container.insertBefore(suggest, container.firstElementChild);
    return suggest;
  }
}
<commit_msg>consistent suggestions ordering (#1528)<commit_after>/**
 * @license
 * Copyright (c) 2018 Google Inc. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt
 * Code distributed by Google as part of this project is also
 * subject to an additional IP rights grant found at
 * http://polymer.github.io/PATENTS.txt
 */
import {assert} from '../platform/assert-web.js';
import {Affordance} from './affordance.js';

export class SuggestionComposer {
  constructor(slotComposer) {
    assert(slotComposer);
    this._affordance = Affordance.forName(slotComposer.affordance);
    // TODO(mmandlis): find a cleaner way to fetch suggestions context.
    this._context = slotComposer._contextSlots.find(context => context.name == 'suggestions').container;
    assert(this._context);

    this._suggestions = [];
    this._suggestionsQueue = [];
    this._updateComplete = null;
  }

  async setSuggestions(suggestions) {
    this._suggestionsQueue.push(suggestions);
    Promise.resolve().then(async () => {
      if (this._updateComplete) {
        await this._updateComplete;
      }
      if (this._suggestionsQueue.length > 0) {
        this._suggestions = this._suggestionsQueue.pop();
        this._suggestionsQueue = [];
        this._updateComplete = this._updateSuggestions(this._suggestions);
      }
    });
  }

  async _updateSuggestions(suggestions) {
    this._affordance.contextClass.clear(this._context);
    let sortedSuggestions = suggestions.sort((s1, s2) => s2.rank - s1.rank);
    for (let suggestion of sortedSuggestions) {
      let suggestionContent =
        await suggestion.description.getRecipeSuggestion(this._affordance.descriptionFormatter);
      assert(suggestionContent, 'No suggestion content available');
      this._affordance.contextClass.createContext(
          this.createSuggestionElement(this._context, suggestion), suggestionContent);
    }
  }

  createSuggestionElement(container, plan) {
    let suggest = Object.assign(document.createElement('suggestion-element'), {plan});
    // TODO(sjmiles): LIFO is weird, iterate top-down elsewhere?
    container.insertBefore(suggest, container.firstElementChild);
    return suggest;
  }
}
<|endoftext|>