<commit_before>// Require Backbone, underscore, if we're on the server.
// @TODO if we use the keyword 'var' in front of Backbone, _, IE will wipe the
// globally defined Backbone and underscore leaving us with broken objects.
// This is obviously not ideal.
if (typeof require !== 'undefined') {
    Backbone = require('backbone-filesystem');
    _ = require('underscore')._;
}

/**
 * Model: Abilities
 *
 * Read-only model describing the abilities of TileLive's Mapnik backend.
 */
var Abilities = Backbone.Model.extend({ url: 'api/abilities' });

/**
 * Model: Settings
 *
 * Settings model. Stores any user-specific configuration related to the app.
 */
var Settings = Backbone.Model.extend({
    /**
     * Model name used for storage.
     */
    type: 'settings',
    /**
     * Override url() method for convenience so we don't always need a
     * collection reference around for CRUD operations on a single model.
     */
    url: function() {
        return 'api/Settings/' + this.id;
    },
    /**
     * Validate settings.
     */
    validate: function(attributes) {
        if (typeof attributes.mode !== 'undefined' && attributes.mode !== 'normal' && attributes.mode !== 'minimal') {
            return 'Invalid editor mode specified.';
        }
    }
});

/**
 * Model: Stylesheet
 *
 * This model is *not* backed directly by the server.
 * It is a child model of Project and is saved serialized as part of the parent
 * Project model.
 */
var Stylesheet = Backbone.Model.extend({
    initialize: function() {
        if (!this.get('data')) {
            this.set({ 'data': '' });
        }
    },
    validate: function(attributes) {
        if (/^[a-z0-9\-_.]+$/i.test(attributes.id) === false) {
            return 'Name must contain no space and only letters, numbers, dashes, underscores and periods.';
        }
    }
});

/**
 * Collection: StylesheetList
 *
 * This collection is *not* backed directly by the server.
 * This collection is a child of the Project model. When it is updated
 * (add/remove events) it updates the attributes of its parent model as well.
 */
var StylesheetList = Backbone.Collection.extend({
    model: Stylesheet,
    initialize: function(models, options) {
        var that = this;
        this.parent = options.parent;
        this.bind('change', function() {
            this.parent.set({ 'Stylesheet': that });
            this.parent.change();
        });
        this.bind('add', function() {
            this.parent.set({ 'Stylesheet': that });
            this.parent.change();
        });
        this.bind('remove', function() {
            this.parent.set({ 'Stylesheet': that });
            this.parent.change();
        });
    }
});

/**
 * Model: Layer
 *
 * This model is *not* backed directly by the server.
 * It is a child model of Project and is saved serialized as part of the parent
 * Project model.
 */
var Layer = Backbone.Model.extend({
    // @TODO either as a feature or a bug, object attributes are not set
    // automatically when passed to the constructor. We set it manually here.
    initialize: function(attributes) {
        this.set({'Datasource': attributes.Datasource});
    },
    validate: function(attributes) {
        if (/^[a-z0-9\-_]+$/i.test(attributes.id) === false) {
            return 'ID must contain only letters, numbers, dashes, and underscores.';
        }
        if (attributes['class'] && /^[a-z0-9\-_ ]+$/i.test(attributes['class']) === false) {
            return 'Class must contain only letters, numbers, dashes, and underscores.';
        }
        if (attributes.Datasource && !attributes.Datasource.file) {
            return 'Supplying a datasource is required.';
        }
    }
});

/**
 * Collection: LayerList
 *
 * This collection is *not* backed directly by the server.
 * This collection is a child of the Project model. When it is updated
 * (add/remove events) it updates the attributes of its parent model as well.
 */
var LayerList = Backbone.Collection.extend({
    model: Layer,
    initialize: function(models, options) {
        var self = this;
        this.parent = options.parent;
        this.bind('change', function() {
            this.parent.set({ 'Layer': self });
            this.parent.change();
        });
        this.bind('add', function() {
            this.parent.set({ 'Layer': self });
            this.parent.change();
        });
        this.bind('remove', function() {
            this.parent.set({ 'Layer': self });
            this.parent.change();
        });
    }
});

/**
 * Model: Datasource
 *
 * This is a read-only model of inspection metadata about a map layer.
 */
var Datasource = Backbone.Model.extend({
    // @TODO either as a feature or a bug, object attributes are not set
    // automatically when passed to the constructor. We set it manually here.
    initialize: function(attributes, options) {
        this.set({'fields': attributes.fields});
    },
    url: function() {
        return 'api/Datasource/' + Base64.urlsafe_encode(this.get('url'));
    }
});

/**
 * Model: Project
 *
 * This model represents a single TileMill project. It is used both client-side
 * and server-side for setting defaults and handling validation.
 */
var Project = Backbone.Model.extend({
    SRS_DEFAULT: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 '
    + '+lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs',
    STYLESHEET_DEFAULT: [{
        id: 'style.mss',
        data: 'Map {\n'
            + '  background-color: #fff;\n'
            + '}\n\n'
            + '#world {\n'
            + '  line-color: #ccc;\n'
            + '  line-width: 0.5;\n'
            + '  polygon-fill: #eee;\n'
            + '}'
    }],
    LAYER_DEFAULT: [{
        id: 'world',
        name: 'world',
        srs: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 '
        + '+lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs',
        geometry: 'polygon',
        Datasource: {
            file: 'http://tilemill-data.s3.amazonaws.com/world_borders_merc.zip',
            type: 'shape'
        }
    }],
    /**
     * Model name used for storage.
     */
    type: 'project',
    /**
     * Model default attributes.
     */
    defaults: {
        '_center': { lat:0, lon:0, zoom:2 },
		'_format': 'png'
    },
    /**
     * Custom setDefaults() method for creating a project with default layers,
     * stylesheets, etc. Note that we do not use Backbone native initialize()
     * or defaults(), both of which make default values far pervasive than the
     * expected use here.
     */
    setDefaults: function() {
        if (!this.get('srs')) {
            this.set({'srs': this.SRS_DEFAULT});
        }
        if (!this.get('Stylesheet')) {
            this.set({
                'Stylesheet': new StylesheetList(this.STYLESHEET_DEFAULT, {
                    parent: this
                })
            });
        }
        if (!this.get('Layer')) {
            this.set({
                'Layer': new LayerList(this.LAYER_DEFAULT, {
                    parent: this
                })
            });
        }
    },
    /**
     * Instantiate StylesheetList and LayerList collections from JSON lists
     * of plain JSON objects.
     */
    parse: function(response) {
        var self = this;
        response.Stylesheet = new StylesheetList(response.Stylesheet ?
                response.Stylesheet :
                [], { parent: this });
        response.Layer = new LayerList(response.Layer ?
                response.Layer : [], { parent: this });
        return response;
    },
    /**
     * Override url() method for convenience so we don't always need a
     * collection reference around for CRUD operations on a single model.
     */
    url: function() {
        return 'api/Project/' + this.id;
    },
    /**
     * Return the base URL of TileMill including a single trailing slash,
     * e.g. http://localhost:8889/ or http://mapbox/tilemill/
     */
    baseURL: function() {
        var baseURL = window.location.protocol + '//' + window.location.host;
        var args = window.location.pathname.split('/');
        // Path already ends with trailing slash.
        if (args[args.length - 1] === '') {
            return baseURL + args.join('/');
        // index.html or similar trailing filename.
        } else if (args[args.length - 1].indexOf('.') !== -1) {
            args.pop();
            return baseURL + args.join('/') + '/';
        // Path beyond domain.
        } else {
            return baseURL + args.join('/') + '/';
        }
    },
    /**
     * Base64 encode this project's MML URL.
     */
    project64: function(options) {
        // `window.location.origin` is not available in all browsers like
        // Firefox. @TODO This approach won't allow TileMill to be installed in
        // a subdirectory. Fix.
        var url = this.baseURL() + this.url();
        if (options.signed) {
            var md5 = new MD5();
            url += '?' + md5.digest(JSON.stringify(this)).substr(0, 6);
        }
        return Base64.urlsafe_encode(url);
    },
    /**
     * Layer URL based on the model URL.
     */
    layerURL: function(options) {
        return this.baseURL();
    },
    /**
     * Native Backbone validation method.
     */
    validate: function(attributes) {
        // Test character set of model ID.
        if (typeof attributes.id !== 'undefined') {
            if (/^[a-z0-9\-_]+$/i.test(attributes.id) === false) {
                return 'Name must contain only letters, numbers, dashes, '
                    + 'and underscores.';
            }
        }
        // Test that there is at least one stylesheet.
        if (typeof attributes.Stylesheet !== 'undefined') {
            var stylesheet = attributes.Stylesheet instanceof StylesheetList
                ? attributes.Stylesheet.models
                : attributes.Stylesheet;
            if (stylesheet.length === 0) {
                return 'No stylesheets found.';
            }
            // Test that each stylesheet has a unique ID.
            var counts = _.reduce(_.pluck(stylesheet, 'id'), function(memo, val) {
                memo[val] = memo[val] ? memo[val] + 1 : 1;
                return memo;
            }, {});
            if (_.max(_.values(counts)) > 1) {
                return 'Stylesheet IDs must be unique.';
            }
        }
    },
    /**
     * Custom validation method that allows for asynchronous processing.
     * Expects options.success and options.error callbacks to be consistent
     * with other Backbone methods.
     */
    validateAsync: function(options) {
        // If client-side, pass-through.
        if (typeof require === 'undefined') {
            return options.success(this, null);
        }

        var mess = require('mess'),
            mapnik = require('mapnik'),
            that = this,
            stylesheets = this.get('Stylesheet'),
            env = {
                returnErrors: true,
                errors: [],
                validation_data: {
                    fonts: mapnik.fonts()
                },
                deferred_externals: [],
                only_validate: true,
                effects: []
            };

        // Hard clone the model JSON before rendering as rendering will change
        // properties (e.g. localize a datasource URL to the filesystem).
        var data = JSON.parse(JSON.stringify(this.toJSON()));
        new mess.Renderer(env)
            .render(data, function(err, output) {
            if (err) {
                options.error(that, err);
            } else {
                options.success(that, null);
            }
        });
    }
});

/**
 * Collection: ProjectList
 *
 * Collection of project models.
 */
var ProjectList = Backbone.Collection.extend({
    model: Project,
    url: 'api/Project',
    /**
     * Model name used for storage.
     */
    type: 'project',
    comparator: function(project) {
        return project.get('id');
    }
});

/**
 * Model: Export
 *
 * Job model.
 */
var Export = Backbone.Model.extend({
    /**
     * Model name used for storage.
     */
    type: 'export',
    initialize: function() {
        if (this.isNew()) {
            this.set({created: +new Date});
            if (typeof MD5 !== 'undefined') {
                var md5 = new MD5();
                var date = new Date();
                md5.digest(JSON.stringify(this)).substr(0, 6);
                this.set({'id': md5.digest(JSON.stringify(this) + date.getTime()).substr(0, 6)});
            }
        }
    },
    /**
     * Override url() method for convenience so we don't always need a
     * collection reference around for CRUD operations on a single model.
     */
    url: function() {
        return 'api/Export/' + this.id;
    },
    defaults: {
        progress: 0,
        status: 'waiting'
    },
    validate: function(attributes) {
        if (attributes.status && ['waiting', 'processing', 'complete', 'error'].indexOf(attributes.status) === -1) {
            return 'Invalid status.';
        }
        if (attributes.progress && (typeof attributes.progress !== 'number' || attributes.progress > 1 || attributes.progress < 0)) {
            return 'Progress must be a value between 0 and 1 (inclusive).';
        }
    },
    /**
     * Generate a download URL for a model.
     */
    downloadURL: function() {
        return (this.get('status') === 'complete') && '/export/download/' + this.get('filename');
    },
    /**
     * Get the duration of the current export job.
     */
    time: function() {
        if (this.get('updated')) {
            var seconds = parseInt((this.get('updated') - this.get('created')) * .001, 10);
            var minutes = parseInt(seconds / 60, 10);
            var remainder = seconds - (minutes * 60);
            if (minutes && remainder) {
                return minutes + ' min ' + remainder + ' sec';
            } else if (minutes) {
                return minutes + ' min';
            } else {
                return seconds + ' sec';
            }
        }
        return '0 sec';
    }
});

/**
 * Collection: ExportList
 *
 * A queue of job models.
 */
var ExportList = Backbone.Collection.extend({
    model: Export,
    url: 'api/Export',
    /**
     * Model name used for storage.
     */
    type: 'export',
    comparator: function(job) {
        return job.get('created');
    }
});

if (typeof module !== 'undefined') {
    module.exports = {
        Project: Project,
        ProjectList: ProjectList,
        Export: Export,
        ExportList: ExportList,
        Datasource: Datasource,
        Settings: Settings
    };
}

/**
 * Model: Asset
 *
 * An external asset, e.g. a shapefile, image, etc.
 */
var Asset = Backbone.Model.extend({});

/**
 * Collection: AssetListS3
 *
 * A list of assets available on an S3 bucket.
 */
var AssetListS3 = Backbone.Collection.extend({
    model: Asset,
    url: '/provider/s3',
    title: 'Amazon S3',
    comparator: function(model) {
        return model.id;
    }
});

/**
 * Collection: AssetListDirectory
 *
 * A list of assets available via local directory.
 */
var AssetListDirectory = Backbone.Collection.extend({
    model: Asset,
    url: '/provider/directory',
    title: 'Local directory',
    comparator: function(model) {
        return model.id;
    }
});

<commit_msg>Use underscore _.indexOf() for IE compatibility.<commit_after>// Require Backbone, underscore, if we're on the server.
// @TODO if we use the keyword 'var' in front of Backbone, _, IE will wipe the
// globally defined Backbone and underscore leaving us with broken objects.
// This is obviously not ideal.
if (typeof require !== 'undefined') {
    Backbone = require('backbone-filesystem');
    _ = require('underscore')._;
}

/**
 * Model: Abilities
 *
 * Read-only model describing the abilities of TileLive's Mapnik backend.
 */
var Abilities = Backbone.Model.extend({ url: 'api/abilities' });

/**
 * Model: Settings
 *
 * Settings model. Stores any user-specific configuration related to the app.
 */
var Settings = Backbone.Model.extend({
    /**
     * Model name used for storage.
     */
    type: 'settings',
    /**
     * Override url() method for convenience so we don't always need a
     * collection reference around for CRUD operations on a single model.
     */
    url: function() {
        return 'api/Settings/' + this.id;
    },
    /**
     * Validate settings.
     */
    validate: function(attributes) {
        if (typeof attributes.mode !== 'undefined' && attributes.mode !== 'normal' && attributes.mode !== 'minimal') {
            return 'Invalid editor mode specified.';
        }
    }
});

/**
 * Model: Stylesheet
 *
 * This model is *not* backed directly by the server.
 * It is a child model of Project and is saved serialized as part of the parent
 * Project model.
 */
var Stylesheet = Backbone.Model.extend({
    initialize: function() {
        if (!this.get('data')) {
            this.set({ 'data': '' });
        }
    },
    validate: function(attributes) {
        if (/^[a-z0-9\-_.]+$/i.test(attributes.id) === false) {
            return 'Name must contain no space and only letters, numbers, dashes, underscores and periods.';
        }
    }
});

/**
 * Collection: StylesheetList
 *
 * This collection is *not* backed directly by the server.
 * This collection is a child of the Project model. When it is updated
 * (add/remove events) it updates the attributes of its parent model as well.
 */
var StylesheetList = Backbone.Collection.extend({
    model: Stylesheet,
    initialize: function(models, options) {
        var that = this;
        this.parent = options.parent;
        this.bind('change', function() {
            this.parent.set({ 'Stylesheet': that });
            this.parent.change();
        });
        this.bind('add', function() {
            this.parent.set({ 'Stylesheet': that });
            this.parent.change();
        });
        this.bind('remove', function() {
            this.parent.set({ 'Stylesheet': that });
            this.parent.change();
        });
    }
});

/**
 * Model: Layer
 *
 * This model is *not* backed directly by the server.
 * It is a child model of Project and is saved serialized as part of the parent
 * Project model.
 */
var Layer = Backbone.Model.extend({
    // @TODO either as a feature or a bug, object attributes are not set
    // automatically when passed to the constructor. We set it manually here.
    initialize: function(attributes) {
        this.set({'Datasource': attributes.Datasource});
    },
    validate: function(attributes) {
        if (/^[a-z0-9\-_]+$/i.test(attributes.id) === false) {
            return 'ID must contain only letters, numbers, dashes, and underscores.';
        }
        if (attributes['class'] && /^[a-z0-9\-_ ]+$/i.test(attributes['class']) === false) {
            return 'Class must contain only letters, numbers, dashes, and underscores.';
        }
        if (attributes.Datasource && !attributes.Datasource.file) {
            return 'Supplying a datasource is required.';
        }
    }
});

/**
 * Collection: LayerList
 *
 * This collection is *not* backed directly by the server.
 * This collection is a child of the Project model. When it is updated
 * (add/remove events) it updates the attributes of its parent model as well.
 */
var LayerList = Backbone.Collection.extend({
    model: Layer,
    initialize: function(models, options) {
        var self = this;
        this.parent = options.parent;
        this.bind('change', function() {
            this.parent.set({ 'Layer': self });
            this.parent.change();
        });
        this.bind('add', function() {
            this.parent.set({ 'Layer': self });
            this.parent.change();
        });
        this.bind('remove', function() {
            this.parent.set({ 'Layer': self });
            this.parent.change();
        });
    }
});

/**
 * Model: Datasource
 *
 * This is a read-only model of inspection metadata about a map layer.
 */
var Datasource = Backbone.Model.extend({
    // @TODO either as a feature or a bug, object attributes are not set
    // automatically when passed to the constructor. We set it manually here.
    initialize: function(attributes, options) {
        this.set({'fields': attributes.fields});
    },
    url: function() {
        return 'api/Datasource/' + Base64.urlsafe_encode(this.get('url'));
    }
});

/**
 * Model: Project
 *
 * This model represents a single TileMill project. It is used both client-side
 * and server-side for setting defaults and handling validation.
 */
var Project = Backbone.Model.extend({
    SRS_DEFAULT: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 '
    + '+lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs',
    STYLESHEET_DEFAULT: [{
        id: 'style.mss',
        data: 'Map {\n'
            + '  background-color: #fff;\n'
            + '}\n\n'
            + '#world {\n'
            + '  line-color: #ccc;\n'
            + '  line-width: 0.5;\n'
            + '  polygon-fill: #eee;\n'
            + '}'
    }],
    LAYER_DEFAULT: [{
        id: 'world',
        name: 'world',
        srs: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 '
        + '+lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs',
        geometry: 'polygon',
        Datasource: {
            file: 'http://tilemill-data.s3.amazonaws.com/world_borders_merc.zip',
            type: 'shape'
        }
    }],
    /**
     * Model name used for storage.
     */
    type: 'project',
    /**
     * Model default attributes.
     */
    defaults: {
        '_center': { lat:0, lon:0, zoom:2 },
		'_format': 'png'
    },
    /**
     * Custom setDefaults() method for creating a project with default layers,
     * stylesheets, etc. Note that we do not use Backbone native initialize()
     * or defaults(), both of which make default values far pervasive than the
     * expected use here.
     */
    setDefaults: function() {
        if (!this.get('srs')) {
            this.set({'srs': this.SRS_DEFAULT});
        }
        if (!this.get('Stylesheet')) {
            this.set({
                'Stylesheet': new StylesheetList(this.STYLESHEET_DEFAULT, {
                    parent: this
                })
            });
        }
        if (!this.get('Layer')) {
            this.set({
                'Layer': new LayerList(this.LAYER_DEFAULT, {
                    parent: this
                })
            });
        }
    },
    /**
     * Instantiate StylesheetList and LayerList collections from JSON lists
     * of plain JSON objects.
     */
    parse: function(response) {
        var self = this;
        response.Stylesheet = new StylesheetList(response.Stylesheet ?
                response.Stylesheet :
                [], { parent: this });
        response.Layer = new LayerList(response.Layer ?
                response.Layer : [], { parent: this });
        return response;
    },
    /**
     * Override url() method for convenience so we don't always need a
     * collection reference around for CRUD operations on a single model.
     */
    url: function() {
        return 'api/Project/' + this.id;
    },
    /**
     * Return the base URL of TileMill including a single trailing slash,
     * e.g. http://localhost:8889/ or http://mapbox/tilemill/
     */
    baseURL: function() {
        var baseURL = window.location.protocol + '//' + window.location.host;
        var args = window.location.pathname.split('/');
        // Path already ends with trailing slash.
        if (args[args.length - 1] === '') {
            return baseURL + args.join('/');
        // index.html or similar trailing filename.
        } else if (_.indexOf(args[args.length - 1], '.') !== -1) {
            args.pop();
            return baseURL + args.join('/') + '/';
        // Path beyond domain.
        } else {
            return baseURL + args.join('/') + '/';
        }
    },
    /**
     * Base64 encode this project's MML URL.
     */
    project64: function(options) {
        // `window.location.origin` is not available in all browsers like
        // Firefox. @TODO This approach won't allow TileMill to be installed in
        // a subdirectory. Fix.
        var url = this.baseURL() + this.url();
        if (options.signed) {
            var md5 = new MD5();
            url += '?' + md5.digest(JSON.stringify(this)).substr(0, 6);
        }
        return Base64.urlsafe_encode(url);
    },
    /**
     * Layer URL based on the model URL.
     */
    layerURL: function(options) {
        return this.baseURL();
    },
    /**
     * Native Backbone validation method.
     */
    validate: function(attributes) {
        // Test character set of model ID.
        if (typeof attributes.id !== 'undefined') {
            if (/^[a-z0-9\-_]+$/i.test(attributes.id) === false) {
                return 'Name must contain only letters, numbers, dashes, '
                    + 'and underscores.';
            }
        }
        // Test that there is at least one stylesheet.
        if (typeof attributes.Stylesheet !== 'undefined') {
            var stylesheet = attributes.Stylesheet instanceof StylesheetList
                ? attributes.Stylesheet.models
                : attributes.Stylesheet;
            if (stylesheet.length === 0) {
                return 'No stylesheets found.';
            }
            // Test that each stylesheet has a unique ID.
            var counts = _.reduce(_.pluck(stylesheet, 'id'), function(memo, val) {
                memo[val] = memo[val] ? memo[val] + 1 : 1;
                return memo;
            }, {});
            if (_.max(_.values(counts)) > 1) {
                return 'Stylesheet IDs must be unique.';
            }
        }
    },
    /**
     * Custom validation method that allows for asynchronous processing.
     * Expects options.success and options.error callbacks to be consistent
     * with other Backbone methods.
     */
    validateAsync: function(options) {
        // If client-side, pass-through.
        if (typeof require === 'undefined') {
            return options.success(this, null);
        }

        var mess = require('mess'),
            mapnik = require('mapnik'),
            that = this,
            stylesheets = this.get('Stylesheet'),
            env = {
                returnErrors: true,
                errors: [],
                validation_data: {
                    fonts: mapnik.fonts()
                },
                deferred_externals: [],
                only_validate: true,
                effects: []
            };

        // Hard clone the model JSON before rendering as rendering will change
        // properties (e.g. localize a datasource URL to the filesystem).
        var data = JSON.parse(JSON.stringify(this.toJSON()));
        new mess.Renderer(env)
            .render(data, function(err, output) {
            if (err) {
                options.error(that, err);
            } else {
                options.success(that, null);
            }
        });
    }
});

/**
 * Collection: ProjectList
 *
 * Collection of project models.
 */
var ProjectList = Backbone.Collection.extend({
    model: Project,
    url: 'api/Project',
    /**
     * Model name used for storage.
     */
    type: 'project',
    comparator: function(project) {
        return project.get('id');
    }
});

/**
 * Model: Export
 *
 * Job model.
 */
var Export = Backbone.Model.extend({
    /**
     * Model name used for storage.
     */
    type: 'export',
    initialize: function() {
        if (this.isNew()) {
            this.set({created: +new Date});
            if (typeof MD5 !== 'undefined') {
                var md5 = new MD5();
                var date = new Date();
                md5.digest(JSON.stringify(this)).substr(0, 6);
                this.set({'id': md5.digest(JSON.stringify(this) + date.getTime()).substr(0, 6)});
            }
        }
    },
    /**
     * Override url() method for convenience so we don't always need a
     * collection reference around for CRUD operations on a single model.
     */
    url: function() {
        return 'api/Export/' + this.id;
    },
    defaults: {
        progress: 0,
        status: 'waiting'
    },
    validate: function(attributes) {
        if (attributes.status && _.indexOf(['waiting', 'processing', 'complete', 'error'], attributes.status) === -1) {
            return 'Invalid status.';
        }
        if (attributes.progress && (typeof attributes.progress !== 'number' || attributes.progress > 1 || attributes.progress < 0)) {
            return 'Progress must be a value between 0 and 1 (inclusive).';
        }
    },
    /**
     * Generate a download URL for a model.
     */
    downloadURL: function() {
        return (this.get('status') === 'complete') && '/export/download/' + this.get('filename');
    },
    /**
     * Get the duration of the current export job.
     */
    time: function() {
        if (this.get('updated')) {
            var seconds = parseInt((this.get('updated') - this.get('created')) * .001, 10);
            var minutes = parseInt(seconds / 60, 10);
            var remainder = seconds - (minutes * 60);
            if (minutes && remainder) {
                return minutes + ' min ' + remainder + ' sec';
            } else if (minutes) {
                return minutes + ' min';
            } else {
                return seconds + ' sec';
            }
        }
        return '0 sec';
    }
});

/**
 * Collection: ExportList
 *
 * A queue of job models.
 */
var ExportList = Backbone.Collection.extend({
    model: Export,
    url: 'api/Export',
    /**
     * Model name used for storage.
     */
    type: 'export',
    comparator: function(job) {
        return job.get('created');
    }
});

if (typeof module !== 'undefined') {
    module.exports = {
        Project: Project,
        ProjectList: ProjectList,
        Export: Export,
        ExportList: ExportList,
        Datasource: Datasource,
        Settings: Settings
    };
}

/**
 * Model: Asset
 *
 * An external asset, e.g. a shapefile, image, etc.
 */
var Asset = Backbone.Model.extend({});

/**
 * Collection: AssetListS3
 *
 * A list of assets available on an S3 bucket.
 */
var AssetListS3 = Backbone.Collection.extend({
    model: Asset,
    url: '/provider/s3',
    title: 'Amazon S3',
    comparator: function(model) {
        return model.id;
    }
});

/**
 * Collection: AssetListDirectory
 *
 * A list of assets available via local directory.
 */
var AssetListDirectory = Backbone.Collection.extend({
    model: Asset,
    url: '/provider/directory',
    title: 'Local directory',
    comparator: function(model) {
        return model.id;
    }
});

<|endoftext|>


<commit_before>var mongoose   = require('mongoose');
var Campground = require('./models/campground');
var Comment    = require('./models/comment');

var data = [
    {
        name: 'Yosemite Camp',
        image: '/img/camp1.jpg',
        description: '
Vivamus quis mi. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor.

In turpis. Fusce fermentum. Phasellus accumsan cursus velit. Sed aliquam ultrices mauris.
'
    },
    {
        name: 'Silicon Valley Camp',
        image: '/img/camp3.jpg',
        description: '
Vivamus quis mi. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor.

In turpis. Fusce fermentum. Phasellus accumsan cursus velit. Sed aliquam ultrices mauris.
'
    },
    {
        name: 'Portland Camp',
        image: '/img/camp2.jpg',
        description: '
Vivamus quis mi. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor.

In turpis. Fusce fermentum. Phasellus accumsan cursus velit. Sed aliquam ultrices mauris.
'
    }
];

function seedDB(){

    // Remove all campgrounds.
    Campground.remove({}, function(err){
        if(err){
            console.log('Error: ' + err);
        } else {
            console.log('All campgrounds removed!');

            // Add a few campground.
            data.forEach(function(camp){
                Campground.create(camp, function(err, newCamp){
                    if(err){
                        console.log('Error: ' + err);
                    } else {
                        console.log('New camp added!');

                        // Create a comment.
                        Comment.create({
                            text: "This is a very short comment for test purposes.",
                            author: "Sherlock Holmes"
                        }, function(err, comment){
                            if(err){
                                console.log('Error: ' + err);
                            } else {
                                newCamp.comments.push(comment);
                                newCamp.save();
                                console.log('New comment added to ' + newCamp.name + ' camp.');
                            }
                        });
                    }
                });
            });
        }
    })
}

module.exports = seedDB;
<commit_msg>Some changes<commit_after>var mongoose   = require('mongoose');
var Campground = require('./models/campground');
var Comment    = require('./models/comment');

var data = [
    {
        name: 'Yosemite Camp',
        image: '/img/camp1.jpg',
        description: '
Vivamus quis mi. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor.

In turpis. Fusce fermentum. Phasellus accumsan cursus velit. Sed aliquam ultrices mauris.
'
    },
    {
        name: 'Silicon Valley Camp',
        image: '/img/camp3.jpg',
        description: '
Vivamus quis mi. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor.

In turpis. Fusce fermentum. Phasellus accumsan cursus velit. Sed aliquam ultrices mauris.
'
    },
    {
        name: 'Portland Camp',
        image: '/img/camp2.jpg',
        description: '
Vivamus quis mi. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor.

In turpis. Fusce fermentum. Phasellus accumsan cursus velit. Sed aliquam ultrices mauris.
'
    }
];

function seedDB(){

    // Remove all campgrounds.
    Campground.remove({}, function(err) {
        if(err){
            console.log('Error: ' + err);
        } else {
            console.log('All campgrounds removed!');

            // Add a few campground.
            data.forEach(function(camp){
                Campground.create(camp, function(err, newCamp){
                    if(err){
                        console.log('Error: ' + err);
                    } else {
                        console.log('New camp added!');

                        // Create a comment.
                        Comment.create({
                            text: "This is a very short comment for test purposes.",
                            author: "Sherlock Holmes"
                        }, function(err, comment){
                            if(err){
                                console.log('Error: ' + err);
                            } else {
                                newCamp.comments.push(comment);
                                newCamp.save();
                                console.log('New comment added to ' + newCamp.name + ' camp.');
                            }
                        });
                    }
                });
            });
        }
    })
}

module.exports = seedDB;
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var fs = require('fs');

var Beer = require('./models/beer');
var Comment = require('./models/comment');
var User = require('./models/user');

var userData = [
  {
    username: "a",
    password: "b",

    name: "Precs",
    self_description: "Water is my favorite beer",
    photo: './public/img/eric.jpg',

    rank_name: "Noob",
    level: 1,
    exp: 24
  },
  {
    username: "b",
    password: "c",

    name: "Coimbra",
    self_description: "I like drinking",
    photo: './public/img/coimbra.jpg',

    rank_name: "Cerva-Boy",

    level: 5,
    exp: 570
  },
  {
    username: "c",
    password: "d",

    name: "Tchola",
    self_description: "I dont drink",
    photo: './public/img/luisclaudio.jpg',

    rank_name: "Virgin",
    level: -1,
    exp: 0
  }
];

var beerData = [
    {
        name: "Stella Artois",
        description: "Aroma suave, notas maltadas e mais de 600 anos de tradição em produção de cerveja.",
        price: 6.49,
        history: "Stella Artois chegou ao Brasil em 2005. Para viabilizar sua produção no país, um mestre-cervejeiro passou cerca de seis meses entre São Paulo e Leuven estudando sua receita, seu processo especial de fabricação, seu ritual de degustação e todos os detalhes que fazem Stella Artois ser Stella Artois.",
        harmonization: "Combina perfeitamente com churrasco e carnes vermelhas.",
        content: 5,
        color: 0,
        ferment: 0
    }
];

function seedDB(){
  seedBeers();
  seedUsers();
}

function seedBeers(){
    Beer.remove({}, function(err){
      console.log("Cleaning beer database");
       if(err) {
           console.log(err);
       }
       else{
       //add a few beers
        beerData.forEach(function(seed){
            Beer.create(seed, function(err, beer){
                if(err){
                    console.log(err);
                } else {
                    console.log("added an beer");
                    //create a comment
                    Comment.create(
                        {
                            text: "Great beer",
                            author: {
                                username: "PrecsBeerKing"
                            }
                        }, function(err, comment) {
                            if(err){
                                console.log(err);
                            } else {
                                beer.comments.push(comment);
                                beer.save();
                                console.log("Created new comment");
                            }
                        }
                    );
                }
            });
        });
      }
    });
}

function seedUsers(){
    User.remove({}, function(err){
    console.log('Cleaning user database');

    if(err)
      console.log(err);
    else{
      userData.forEach(function(seed){
        User.register(seed, seed.password, function(err,user){
          if(err){
            console.log(err);
          }
          else{
            console.log("Added new user");
          }
        });
      });
    }
  });
}

module.exports = seedDB;
<commit_msg>Fix images paths<commit_after>var mongoose = require('mongoose');
var fs = require('fs');

var Beer = require('./models/beer');
var Comment = require('./models/comment');
var User = require('./models/user');

var userData = [
  {
    username: "a",
    password: "b",

    name: "Precs",
    self_description: "Water is my favorite beer",
    photo: 'img/eric.jpg',

    rank_name: "Noob",
    level: 1,
    exp: 24
  },
  {
    username: "b",
    password: "c",

    name: "Coimbra",
    self_description: "I like drinking",
    photo: 'img/coimbra.jpg',

    rank_name: "Cerva-Boy",

    level: 5,
    exp: 570
  },
  {
    username: "c",
    password: "d",

    name: "Tchola",
    self_description: "I dont drink",
    photo: 'img/luisclaudio.jpg',

    rank_name: "Virgin",
    level: -1,
    exp: 0
  }
];

var beerData = [
    {
        name: "Stella Artois",
        description: "Aroma suave, notas maltadas e mais de 600 anos de tradição em produção de cerveja.",
        price: 6.49,
        history: "Stella Artois chegou ao Brasil em 2005. Para viabilizar sua produção no país, um mestre-cervejeiro passou cerca de seis meses entre São Paulo e Leuven estudando sua receita, seu processo especial de fabricação, seu ritual de degustação e todos os detalhes que fazem Stella Artois ser Stella Artois.",
        harmonization: "Combina perfeitamente com churrasco e carnes vermelhas.",
        content: 5,
        color: 0,
        ferment: 0
    }
];

function seedDB(){
  seedBeers();
  seedUsers();
}

function seedBeers(){
    Beer.remove({}, function(err){
      console.log("Cleaning beer database");
       if(err) {
           console.log(err);
       }
       else{
       //add a few beers
        beerData.forEach(function(seed){
            Beer.create(seed, function(err, beer){
                if(err){
                    console.log(err);
                } else {
                    console.log("added an beer");
                    //create a comment
                    Comment.create(
                        {
                            text: "Great beer",
                            author: {
                                username: "PrecsBeerKing"
                            }
                        }, function(err, comment) {
                            if(err){
                                console.log(err);
                            } else {
                                beer.comments.push(comment);
                                beer.save();
                                console.log("Created new comment");
                            }
                        }
                    );
                }
            });
        });
      }
    });
}

function seedUsers(){
    User.remove({}, function(err){
    console.log('Cleaning user database');

    if(err)
      console.log(err);
    else{
      userData.forEach(function(seed){
        User.register(seed, seed.password, function(err,user){
          if(err){
            console.log(err);
          }
          else{
            console.log("Added new user");
          }
        });
      });
    }
  });
}

module.exports = seedDB;
<|endoftext|>


<commit_before>import { Component } from 'react'
import styled from 'styled-components'
import h from 'react-hyperscript'
import {Headline, Label, Body} from '../Global/Text.ts'
import {ButtonPrimary} from '../Global/Buttons.ts'

export class ConceptCard extends Component {
  render () {
    // NOTE once the concept definition process is more defined we will want to
    // use the learnMore field that the ipfs description of a concept provides

    // set LearnMore link if it's provided by the concept data
    // let LearnMore = null
    // if (this.props.conceptData.learnMore) {
    //   LearnMore = h(cardButtonSecondary, {href: this.props.conceptData.learnMore, target: '_blank'}, 'Learn')
    // }
    return h(cardContainer, [
      h(cardContainerInfo, [
        h(cardTextObject, [
          h(Label, 'Concept'),
          h(Headline, this.props.conceptData.name)
        ])
      ]),
      h(cardContainerDescription, [
        h(cardObjectDescription, [
          h(Body, this.props.conceptData.description)
        ]),
        h(cardContainerButtons, [
          h(ButtonPrimary, {onClick: this.props.selectConcept.bind(this), id: this.props.conceptAddress}, 'Get Assessed')
        ])
      ])
    ])
  }
}

export default ConceptCard

// styles

const cardContainer = styled('div').attrs({
  className: 'flex flex-column ma3 br2 shadow-4'
})`height: 420px; width: 300px;background: linear-gradient(180.1deg, #FFFFFF 0.05%, #E9F7FD 52.48%, #CFF9EF 85.98%);
`
const cardContainerInfo = styled('div').attrs({
  className: 'flex justify-between flex-column w-100 pa3'
})`
height: 60%;
`

const cardTextObject = styled('div').attrs({
  className: 'flex flex-column tl'
})`
`

const cardContainerDescription = styled('div').attrs({
  className: 'relative flex content-between flex-column w-100'
})`
height: 40%;
background-color: #D3ECF7;
`

const cardObjectDescription = styled('div').attrs({
  className: 'flex flex-column h-100 pl3 pa3'
})`
`

const cardContainerButtons = styled('div').attrs({className: 'flex flex-row justify-center w-100 pb3 ph3'})`
`

// const cardButtonSecondary = styled('div').attrs({
//   className: 'flex self-end ph4 pv2 fw4 f5 items-center align-center br-pill dark-blue'
// })`box-shadow: 0px 0px 0px 1px hsla(214, 100%, 31%, 0.1);
// `
<commit_msg>Updated conceptCard structure, some styling to better ref Figma & accommodate global components<commit_after>import { Component } from 'react'
import styled from 'styled-components'
import h from 'react-hyperscript'
import {Headline, Label, Body} from '../Global/Text.ts'
import {ButtonPrimary} from '../Global/Buttons.ts'

export class ConceptCard extends Component {
  render () {
    // NOTE once the concept definition process is more defined we will want to
    // use the learnMore field that the ipfs description of a concept provides

    // set LearnMore link if it's provided by the concept data
    // let LearnMore = null
    // if (this.props.conceptData.learnMore) {
    //   LearnMore = h(cardButtonSecondary, {href: this.props.conceptData.learnMore, target: '_blank'}, 'Learn')
    // }
    return h(cardContainer, [
      h(cardContainerInfo, [
        h(cardTextObject, [
          h(Label, 'Concept'),
          h(Headline, this.props.conceptData.name)
        ])
      ]),
      h(cardContainerDescription, [
        h(Body, this.props.conceptData.description),
        h(cardContainerButtons, [
          h(ButtonPrimary, {onClick: this.props.selectConcept.bind(this), id: this.props.conceptAddress}, 'Get Assessed')
        ])
      ])
    ])
  }
}

export default ConceptCard

// styles

const cardContainer = styled('div').attrs({
  className: 'flex flex-column ma3 br2 shadow-4'
})`
height: 420px;
width: 300px;
background: linear-gradient(180deg, #FFFFFF 0%, #BFD4FF 100%);
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.5);
`
const cardContainerInfo = styled('div').attrs({
  className: 'flex justify-between flex-column w-100 pa3'
})`
height: 50%;
`

const cardTextObject = styled('div').attrs({
  className: 'flex flex-column tl'
})`
`

const cardContainerDescription = styled('div').attrs({
  className: 'relative flex content-between flex-column w-100 justify-between pa3'
})`
height: 50%;
`

const cardObjectDescription = styled('div').attrs({
  className: 'flex flex-column h-100 pl3 pa3'
})`
`

const cardContainerButtons = styled('div').attrs({className: 'flex flex-row justify-center w-100'})`
`

// const cardButtonSecondary = styled('div').attrs({
//   className: 'flex self-end ph4 pv2 fw4 f5 items-center align-center br-pill dark-blue'
// })`box-shadow: 0px 0px 0px 1px hsla(214, 100%, 31%, 0.1);
// `
<|endoftext|>


<commit_before>import Ember from 'ember';

var KEYS = {
  TAB: 9,
  ENTER: 13,
  ESC: 27,
  SPACE: 32,
  UP: 38,
  DOWN: 40
};

/* TODO:
  o blur dismisses suggestions (buggy)
  o handle * in numeric range query
  o handle conversion of "false" -> bool -> numeric value
  o safari & IE testing
    * safari: box only shows first time
*/

export default Ember.Component.extend({
  tagName: 'div',
  classNames: ['auto-complete'],
  value: '',
  terms: [],
  suggestions: [],

  _selectedSuggestionIndex: -1,

  init: function() {
    this._super();

    // encourage property observers to start:
    this.get('currentTerm');
  },

  _cursorIndex: function() {
    var input = this.get('_text');
    if (input) {
      return input.caret().start;
    }
    return 0;
  }.property('value'),

  currentTerm: function() {
    var value = this.get('value');
    var range = this._getTermRange();

    if (range.start < 0) {
      return '';
    }

    return value.substring(range.start, range.end);
  }.property('_cursorIndex', 'value'),

  _getTermRange: function() {
    var value = this.get('value');
    var end = this.get('_cursorIndex');
    var start = end - 1;
    while (start > 0 && this._isValidTermChar(value[start])) {
      if (value[start] === ':') {
        return { start: -1, end: -1 };
      }
      start--;
    }

    if (!this._isValidTermChar(value[start])) {
      start++;
    }

    return { start: start, end: end };
  },

  _isValidTermChar: function(c) {
    return c !== ' ' && c !== '+' && c !== '-' && c !== '(';
  },

  _findSuggestions: function() {
    var term = this.get('currentTerm').toLowerCase();

    var matches = term === '' ? [] : this.get('terms').filter(function(i) {
      return i.toLowerCase().indexOf(term) >= 0;
    }).sort(function(a, b) {
      var aStartsWith = a.toLowerCase().indexOf(term) === 0;
      var bStartsWith = b.toLowerCase().indexOf(term) === 0;
      if (aStartsWith && !bStartsWith) {
        return -1;
      } else if (bStartsWith && !aStartsWith) {
        return 1;
      }

      return a.length - b.length;
    });

    this.set('suggestions', matches);
  }.observes('currentTerm'),

  _showAutocompleteSuggestions: function() {
    var self = this;

    var list = this.$("ol");

    list.empty();

    this.set('_selectedSuggestionIndex', -1);

    this.get('suggestions').forEach(function(i) {
      list.append('
' + i + '
');
    });

    list.children('li').on('click', function() {
      var value = Ember.$(this).text();
      self.send('selectTerm', value);
    });
  }.observes('suggestions'),

  _attachEvents: function() {
    var self = this;
    var text = this.$('input[type=text]');
    var list = this.$('ol');

    text.on('keydown.auto-complete', function(e) {
      if (e.which === KEYS.DOWN) {
        self.send('nextSuggestion');
      } else if (e.which === KEYS.UP) {
        self.send('previousSuggestion');
      } else if (e.which === KEYS.ENTER || e.which === KEYS.TAB) {
        var selection = self.$('li.selected').first().text();
        if (selection !== '') {
          self.send('selectTerm', selection);
        } else if (e.which === KEYS.ENTER) {
          self.sendAction();
        } else {
          return true;
        }
      } else if (e.which === KEYS.SPACE && e.ctrlKey) {
        self.send('suggestAll');
      } else if (e.which === KEYS.ESC) {
        self.send('hideSuggestions');
      } else {
        return true;
      }

      e.preventDefault();
      return false;
    });

    text.on('blur.auto-complete', function() {
      if (self.get('_blurIntoOptions') === true) {
        self.set('_blurIntoOptions', false);
        return;
      }

      Ember.run.schedule('afterRender', function() {
        console.log('blue: hide list');
        self.send('hideSuggestions');
      });
    });

    list.on('mousedown.auto-complete', function() {
      self.set('_blurIntoOptions', true);
    });

    this.set('_text', text);
  }.on('didInsertElement'),

  _unattachEvents: function() {
    var text = this.$('input[type=text]');
    text.off('keydown.auto-complete');
    text.off('blur.auto-complete');
  }.on('willDestroyElement'),

  _advanceSuggestion: function(delta, absolute) {
    var i = this.get('_selectedSuggestionIndex');
    var next = absolute !== undefined ? absolute : i + delta;

    var opts = this.$('li');
    if (opts.length === 0) {
      return;
    }

    if (next < 0) {
      next = 0;
    } else if (next >= opts.length) {
      next = opts.length - 1;
    }

    if (i >= 0 && i < opts.length) {
      Ember.$(opts[i]).removeClass('selected');
    }
    Ember.$(opts[next]).addClass('selected');

    this.set('_selectedSuggestionIndex', next);
  },

  _scrollToSelectedIndex: function() {
    var list = this.$('ol.auto-complete');
    var selected = this.$('li.selected');
    if (selected.length !== 1) {
      list.scrollTop(0);
      return;
    }

    var offset = selected.offset().top - list.offset().top;

    if (offset < 0) {
      list.scrollTop(list.scrollTop() + offset);
    } else if (offset + selected.height() > list.height()) {
      var pos = offset + selected.height() - list.height();
      list.scrollTop(list.scrollTop() + pos);
    }
  }.observes('_selectedSuggestionIndex'),

  actions: {
    selectTerm: function(term) {
      var value = this.get('value');
      var range = this._getTermRange();
      var up = value.substring(0, range.start) + term + ':';

      var caretPosition = up.length;

      if (range.end < value.length) {
        var end = value.substring(range.end, value.length);
        if (end.length > 0 && end[0] !== ' ') {
          end = ' ' + end;
        }
        up += end;
      }

      this.set('value', up);

      var list = this.$("ol");
      list.empty();

      var text = this.get('_text');

      Ember.run.schedule('afterRender', function() {
        text.caret(caretPosition, caretPosition);
      });
    },

    nextSuggestion: function() {
      this._advanceSuggestion(1);
    },

    previousSuggestion: function() {
      this._advanceSuggestion(-1);
    },

    suggestAll: function() {
      this.set('suggestions', this.get('terms').sort());
      this._advanceSuggestion(0, 0);
    },

    hideSuggestions: function() {
      this.set('suggestions', []);
    }
  }
});
<commit_msg>Remove spurious console.log<commit_after>import Ember from 'ember';

var KEYS = {
  TAB: 9,
  ENTER: 13,
  ESC: 27,
  SPACE: 32,
  UP: 38,
  DOWN: 40
};

/* TODO:
  o blur dismisses suggestions (buggy)
  o handle * in numeric range query
  o handle conversion of "false" -> bool -> numeric value
  o safari & IE testing
    * safari: box only shows first time
*/

export default Ember.Component.extend({
  tagName: 'div',
  classNames: ['auto-complete'],
  value: '',
  terms: [],
  suggestions: [],

  _selectedSuggestionIndex: -1,

  init: function() {
    this._super();

    // encourage property observers to start:
    this.get('currentTerm');
  },

  _cursorIndex: function() {
    var input = this.get('_text');
    if (input) {
      return input.caret().start;
    }
    return 0;
  }.property('value'),

  currentTerm: function() {
    var value = this.get('value');
    var range = this._getTermRange();

    if (range.start < 0) {
      return '';
    }

    return value.substring(range.start, range.end);
  }.property('_cursorIndex', 'value'),

  _getTermRange: function() {
    var value = this.get('value');
    var end = this.get('_cursorIndex');
    var start = end - 1;
    while (start > 0 && this._isValidTermChar(value[start])) {
      if (value[start] === ':') {
        return { start: -1, end: -1 };
      }
      start--;
    }

    if (!this._isValidTermChar(value[start])) {
      start++;
    }

    return { start: start, end: end };
  },

  _isValidTermChar: function(c) {
    return c !== ' ' && c !== '+' && c !== '-' && c !== '(';
  },

  _findSuggestions: function() {
    var term = this.get('currentTerm').toLowerCase();

    var matches = term === '' ? [] : this.get('terms').filter(function(i) {
      return i.toLowerCase().indexOf(term) >= 0;
    }).sort(function(a, b) {
      var aStartsWith = a.toLowerCase().indexOf(term) === 0;
      var bStartsWith = b.toLowerCase().indexOf(term) === 0;
      if (aStartsWith && !bStartsWith) {
        return -1;
      } else if (bStartsWith && !aStartsWith) {
        return 1;
      }

      return a.length - b.length;
    });

    this.set('suggestions', matches);
  }.observes('currentTerm'),

  _showAutocompleteSuggestions: function() {
    var self = this;

    var list = this.$("ol");

    list.empty();

    this.set('_selectedSuggestionIndex', -1);

    this.get('suggestions').forEach(function(i) {
      list.append('
' + i + '
');
    });

    list.children('li').on('click', function() {
      var value = Ember.$(this).text();
      self.send('selectTerm', value);
    });
  }.observes('suggestions'),

  _attachEvents: function() {
    var self = this;
    var text = this.$('input[type=text]');
    var list = this.$('ol');

    text.on('keydown.auto-complete', function(e) {
      if (e.which === KEYS.DOWN) {
        self.send('nextSuggestion');
      } else if (e.which === KEYS.UP) {
        self.send('previousSuggestion');
      } else if (e.which === KEYS.ENTER || e.which === KEYS.TAB) {
        var selection = self.$('li.selected').first().text();
        if (selection !== '') {
          self.send('selectTerm', selection);
        } else if (e.which === KEYS.ENTER) {
          self.sendAction();
        } else {
          return true;
        }
      } else if (e.which === KEYS.SPACE && e.ctrlKey) {
        self.send('suggestAll');
      } else if (e.which === KEYS.ESC) {
        self.send('hideSuggestions');
      } else {
        return true;
      }

      e.preventDefault();
      return false;
    });

    text.on('blur.auto-complete', function() {
      if (self.get('_blurIntoOptions') === true) {
        self.set('_blurIntoOptions', false);
        return;
      }

      Ember.run.schedule('afterRender', function() {
        self.send('hideSuggestions');
      });
    });

    list.on('mousedown.auto-complete', function() {
      self.set('_blurIntoOptions', true);
    });

    this.set('_text', text);
  }.on('didInsertElement'),

  _unattachEvents: function() {
    var text = this.$('input[type=text]');
    text.off('keydown.auto-complete');
    text.off('blur.auto-complete');
  }.on('willDestroyElement'),

  _advanceSuggestion: function(delta, absolute) {
    var i = this.get('_selectedSuggestionIndex');
    var next = absolute !== undefined ? absolute : i + delta;

    var opts = this.$('li');
    if (opts.length === 0) {
      return;
    }

    if (next < 0) {
      next = 0;
    } else if (next >= opts.length) {
      next = opts.length - 1;
    }

    if (i >= 0 && i < opts.length) {
      Ember.$(opts[i]).removeClass('selected');
    }
    Ember.$(opts[next]).addClass('selected');

    this.set('_selectedSuggestionIndex', next);
  },

  _scrollToSelectedIndex: function() {
    var list = this.$('ol.auto-complete');
    var selected = this.$('li.selected');
    if (selected.length !== 1) {
      list.scrollTop(0);
      return;
    }

    var offset = selected.offset().top - list.offset().top;

    if (offset < 0) {
      list.scrollTop(list.scrollTop() + offset);
    } else if (offset + selected.height() > list.height()) {
      var pos = offset + selected.height() - list.height();
      list.scrollTop(list.scrollTop() + pos);
    }
  }.observes('_selectedSuggestionIndex'),

  actions: {
    selectTerm: function(term) {
      var value = this.get('value');
      var range = this._getTermRange();
      var up = value.substring(0, range.start) + term + ':';

      var caretPosition = up.length;

      if (range.end < value.length) {
        var end = value.substring(range.end, value.length);
        if (end.length > 0 && end[0] !== ' ') {
          end = ' ' + end;
        }
        up += end;
      }

      this.set('value', up);

      var list = this.$("ol");
      list.empty();

      var text = this.get('_text');

      Ember.run.schedule('afterRender', function() {
        text.caret(caretPosition, caretPosition);
      });
    },

    nextSuggestion: function() {
      this._advanceSuggestion(1);
    },

    previousSuggestion: function() {
      this._advanceSuggestion(-1);
    },

    suggestAll: function() {
      this.set('suggestions', this.get('terms').sort());
      this._advanceSuggestion(0, 0);
    },

    hideSuggestions: function() {
      this.set('suggestions', []);
    }
  }
});
<|endoftext|>


<commit_before>import Component from '@ember/component';

import { task } from 'ember-concurrency';

export default Component.extend({
  tagName: '',
  isAccepted: false,
  isDeclined: false,
  isError: false,
  inviteError: 'default error message',

  acceptInvitationTask: task(function* () {
    this.invite.set('accepted', true);

    try {
      yield this.invite.save();
      this.set('isAccepted', true);
    } catch (error) {
      this.set('isError', true);
      if (error.errors) {
        this.set('inviteError', `Error in accepting invite: ${error.errors[0].detail}`);
      } else {
        this.set('inviteError', 'Error in accepting invite');
      }
    }
  }),

  declineInvitationTask: task(function* () {
    this.invite.set('accepted', false);

    try {
      yield this.invite.save();
      this.set('isDeclined', true);
    } catch (error) {
      this.set('isError', true);
      if (error.errors) {
        this.set('inviteError', `Error in declining invite: ${error.errors[0].detail}`);
      } else {
        this.set('inviteError', 'Error in declining invite');
      }
    }
  }),
});
<commit_msg>PendingOwnerInviteRow: Convert to ES6 class<commit_after>import Component from '@ember/component';

import { task } from 'ember-concurrency';

export default class PendingOwnerInviteRow extends Component {
  tagName = '';

  isAccepted = false;
  isDeclined = false;
  isError = false;
  inviteError = 'default error message';

  @task(function* () {
    this.invite.set('accepted', true);

    try {
      yield this.invite.save();
      this.set('isAccepted', true);
    } catch (error) {
      this.set('isError', true);
      if (error.errors) {
        this.set('inviteError', `Error in accepting invite: ${error.errors[0].detail}`);
      } else {
        this.set('inviteError', 'Error in accepting invite');
      }
    }
  })
  acceptInvitationTask;

  @task(function* () {
    this.invite.set('accepted', false);

    try {
      yield this.invite.save();
      this.set('isDeclined', true);
    } catch (error) {
      this.set('isError', true);
      if (error.errors) {
        this.set('inviteError', `Error in declining invite: ${error.errors[0].detail}`);
      } else {
        this.set('inviteError', 'Error in declining invite');
      }
    }
  })
  declineInvitationTask;
}
<|endoftext|>


<commit_before>(function () {
    "use strict";

    angular
        .module("scrum_retroboard")
        .service("messagesService", ["$http", "userService", "sessionService", messagesService]);

    function messagesService($http, userService, sessionService) {
        var config = {
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            }
        };

        var category = "";
        var messagesApiUrl = "https://scrum-retroboard.herokuapp.com/messages/";
        var messageText = "";
        var sessionApiUrl = "https://endava-scrum-app-staging.azurewebsites.net/api/Sessions/";

        this.addMessageToSession = addMessageToSession;
        this.getMessagesByCategory = getMessagesByCategory;
        this.updateMessage = updateMessage;

        function addMessageToSession(messageText, _category) {
            var message = $.param({
                content: messageText,
                userId: userService.getUserId(),
                category: _category
            });

            return $http
                .post(messagesApiUrl + "new", message, config)
                .success(successCallback)
                .error(errorCallback);

            function successCallback(response) {
                return response.data;
            }

            function errorCallback(response) {
                return response.data;
            }
        }

        function getMessagesByCategory(category) {
            return $http({
                method: "GET",
                url: messagesApiUrl + "all/" + category
            });
        }

        function updateMessage(_content, _messageId) {
            var message = $.param({
                content: _content,
                messageId: _messageId
            });

            return $http
                .post(messagesApiUrl + "update/", message, config)
                .success(successCallback)
                .error(errorCallback);

            function successCallback(response) {
                return response.data;
            }

            function errorCallback(response) {
                console.log(response.data);
            }
        }
    }
}());<commit_msg>Minor style changes<commit_after>(function () {
    "use strict";

    angular
        .module("scrum_retroboard")
        .service("messagesService", ["$http", "userService", "sessionService", messagesService]);

    function messagesService($http, userService, sessionService) {
        var config = {
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            }
        };

        var category = "";
        var messagesApiUrl = "https://scrum-retroboard.herokuapp.com/messages/";
        var messageText = "";
        var sessionApiUrl = "https://endava-scrum-app-staging.azurewebsites.net/api/Sessions/";

        this.addMessageToSession = addMessageToSession;
        this.getMessagesByCategory = getMessagesByCategory;
        this.updateMessage = updateMessage;

        function addMessageToSession(messageText, _category) {
            var message = $.param({
                content: messageText,
                userId: userService.getUserId(),
                category: _category
            });

            return $http
                .post(messagesApiUrl + "new", message, config)
                .success(successCallback)
                .error(errorCallback);

            function successCallback(response) {
                return response.data;
            }

            function errorCallback(response) {
                return response.data;
            }
        }

        function getMessagesByCategory(category) {
            return $http({
                method: "GET",
                url: messagesApiUrl + "all/" + category
            });
        }

        function updateMessage(_content, _messageId) {
            var message = $.param({
                content: _content,
                messageId: _messageId
            });

            return $http
                .post(messagesApiUrl + "update/", message, config)
                .success(successCallback)
                .error(errorCallback);

            function successCallback(response) {
                return response.data;
            }

            function errorCallback(response) {
                return response.data;
            }
        }
    }
}());<|endoftext|>


<commit_before>// NOTE: module only included in development mode, so can use dev-dependencies

import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

// eslint-disable-next-line import/no-extraneous-dependencies
import { persistState } from 'redux-devtools';

import rootReducer from '../reducers';
import DevTools from '../containers/dev_tools';

const middleware = [thunk];

const enhancer = compose(
  applyMiddleware(...middleware),
  DevTools.instrument(),
  persistState(window.location.href.match(/[?&]debug_session=([^&#]+)\b/))
);

const configureStore = initialState => {
  const store = createStore(rootReducer, initialState, enhancer);

  if (module.hot) {
    // eslint-disable-next-line global-require
    module.hot.accept('../reducers', () => store.replaceReducer(require('../reducers').default));
  }

  return store;
};

export default configureStore;
<commit_msg>enable redux chrome devtools<commit_after>import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { identity } from 'ramda';

// eslint-disable-next-line import/no-extraneous-dependencies
import { persistState } from 'redux-devtools';

import rootReducer from '../reducers';

const middleware = [thunk];

/* eslint-disable no-underscore-dangle */
const devToolsEnhancer = window.__REDUX_DEVTOOLS_EXTENSION__
  ? window.__REDUX_DEVTOOLS_EXTENSION__()
  : identity;
/* eslint-enable */

const enhancer = compose(
  applyMiddleware(...middleware),
  devToolsEnhancer,
  persistState(window.location.href.match(/[?&]debug_session=([^&#]+)\b/))
);

const configureStore = initialState => {
  const store = createStore(rootReducer, initialState, enhancer);

  if (module.hot) {
    // eslint-disable-next-line global-require
    module.hot.accept('../reducers', () => store.replaceReducer(require('../reducers').default));
  }

  return store;
};

export default configureStore;
<|endoftext|>


<commit_before>var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello Travis!\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337');
<commit_msg>no longer need to bypass travis to keep him happy<commit_after><|endoftext|>


<commit_before>/*
 *
 * AddHintContainer reducer
 *
 */

import { fromJS } from 'immutable';
import {
  DEFAULT_ACTION, GET_COORDINATES, GET_COORDINATES_SUCCESS, SUBMIT_COORDINATES,
  SUBMIT_COORDINATES_SUCCESS,
} from './constants';

const initialState = fromJS({
  loading: false,
  rdx: 0,
  rdy: 0,
  wgs: false,
  subarea: false,
  address: false,
  submittingSuccess: false,
  submitting: false,
});

function addHintContainerReducer(state = initialState, action) {
  switch (action.type) {
    case GET_COORDINATES:
      return state
        .set('loading', true)
        .set('submittingSuccess', false)
        .set('rdx', action.rdx)
        .set('rdy', action.rdy);
    case GET_COORDINATES_SUCCESS:
      console.log(action.response);
      return state
        .set('loading', false)
        .set('subarea', action.response.subarea)
        .set('address', action.response.address)
        .set('wgs', action.response.wgs);
    case SUBMIT_COORDINATES:
      return state
        .set('submitting', true);
    case SUBMIT_COORDINATES_SUCCESS:
      return state
        .set('submitting', false)
        .set('subarea', false)
        .set('address', false)
        .set('wgs', false)
        .set('rdx', false)
        .set('submittingSuccess', true)
        .set('rdy', false);
    case DEFAULT_ACTION:
      return state;
    default:
      return state;
  }
}

export default addHintContainerReducer;
<commit_msg>Removed console.log in AddHint<commit_after>/*
 *
 * AddHintContainer reducer
 *
 */

import { fromJS } from 'immutable';
import {
  DEFAULT_ACTION, GET_COORDINATES, GET_COORDINATES_SUCCESS, SUBMIT_COORDINATES,
  SUBMIT_COORDINATES_SUCCESS,
} from './constants';

const initialState = fromJS({
  loading: false,
  rdx: 0,
  rdy: 0,
  wgs: false,
  subarea: false,
  address: false,
  submittingSuccess: false,
  submitting: false,
});

function addHintContainerReducer(state = initialState, action) {
  switch (action.type) {
    case GET_COORDINATES:
      return state
        .set('loading', true)
        .set('submittingSuccess', false)
        .set('rdx', action.rdx)
        .set('rdy', action.rdy);
    case GET_COORDINATES_SUCCESS:
      return state
        .set('loading', false)
        .set('subarea', action.response.subarea)
        .set('address', action.response.address)
        .set('wgs', action.response.wgs);
    case SUBMIT_COORDINATES:
      return state
        .set('submitting', true);
    case SUBMIT_COORDINATES_SUCCESS:
      return state
        .set('submitting', false)
        .set('subarea', false)
        .set('address', false)
        .set('wgs', false)
        .set('rdx', false)
        .set('submittingSuccess', true)
        .set('rdy', false);
    case DEFAULT_ACTION:
      return state;
    default:
      return state;
  }
}

export default addHintContainerReducer;
<|endoftext|>


<commit_before>/**
 * @module ol/format/MVT
 */
//FIXME Implement projection handling

import {inherits} from '../util.js';
import {assert} from '../asserts.js';
import PBF from 'pbf';
import FeatureFormat, {transformWithOptions} from '../format/Feature.js';
import FormatType from '../format/FormatType.js';
import GeometryLayout from '../geom/GeometryLayout.js';
import GeometryType from '../geom/GeometryType.js';
import LineString from '../geom/LineString.js';
import MultiLineString from '../geom/MultiLineString.js';
import MultiPoint from '../geom/MultiPoint.js';
import MultiPolygon from '../geom/MultiPolygon.js';
import Point from '../geom/Point.js';
import Polygon from '../geom/Polygon.js';
import {linearRingIsClockwise} from '../geom/flat/orient.js';
import Projection from '../proj/Projection.js';
import Units from '../proj/Units.js';
import RenderFeature from '../render/Feature.js';

/**
 * @typedef {Object} Options
 * @property {function((module:ol/geom/Geometry|Object.<string,*>)=)|function(module:ol/geom/GeometryType,Array.<number>,(Array.<number>|Array.<Array.<number>>),Object.<string,*>,number)} [featureClass]
 * Class for features returned by {@link module:ol/format/MVT#readFeatures}. Set to
 * {@link module:ol/Feature~Feature} to get full editing and geometry support at the cost of
 * decreased rendering performance. The default is {@link module:ol/render/Feature~RenderFeature},
 * which is optimized for rendering and hit detection.
 * @property {string} [geometryName='geometry'] Geometry name to use when creating
 * features.
 * @property {string} [layerName='layer'] Name of the feature attribute that
 * holds the layer name.
 * @property {Array.<string>} [layers] Layers to read features from. If not
 * provided, features will be read from all layers.
 */

/**
 * @classdesc
 * Feature format for reading data in the Mapbox MVT format.
 *
 * @extends {module:ol/format/Feature}
 * @param {module:ol/format/MVT~Options=} opt_options Options.
 * @api
 */
class MVT {

  /**
   * @param {module:ol/format/MVT~Options=} opt_options Options.
   */
  constructor(opt_options) {

    FeatureFormat.call(this);

    const options = opt_options ? opt_options : {};

    /**
     * @type {module:ol/proj/Projection}
     */
    this.dataProjection = new Projection({
      code: '',
      units: Units.TILE_PIXELS
    });

    /**
     * @private
     * @type {function((module:ol/geom/Geometry|Object.<string,*>)=)|
     *     function(module:ol/geom/GeometryType,Array.<number>,
     *         (Array.<number>|Array.<Array.<number>>),Object.<string,*>,number)}
     */
    this.featureClass_ = options.featureClass ?
      options.featureClass : RenderFeature;

    /**
     * @private
     * @type {string|undefined}
     */
    this.geometryName_ = options.geometryName;

    /**
     * @private
     * @type {string}
     */
    this.layerName_ = options.layerName ? options.layerName : 'layer';

    /**
     * @private
     * @type {Array.<string>}
     */
    this.layers_ = options.layers ? options.layers : null;

    /**
     * @private
     * @type {module:ol/extent~Extent}
     */
    this.extent_ = null;

  }

  /**
   * Read the raw geometry from the pbf offset stored in a raw feature's geometry
   * property.
   * @suppress {missingProperties}
   * @param {Object} pbf PBF.
   * @param {Object} feature Raw feature.
   * @param {Array.<number>} flatCoordinates Array to store flat coordinates in.
   * @param {Array.<number>} ends Array to store ends in.
   * @private
   */
  readRawGeometry_(pbf, feature, flatCoordinates, ends) {
    pbf.pos = feature.geometry;

    const end = pbf.readVarint() + pbf.pos;
    let cmd = 1;
    let length = 0;
    let x = 0;
    let y = 0;
    let coordsLen = 0;
    let currentEnd = 0;

    while (pbf.pos < end) {
      if (!length) {
        const cmdLen = pbf.readVarint();
        cmd = cmdLen & 0x7;
        length = cmdLen >> 3;
      }

      length--;

      if (cmd === 1 || cmd === 2) {
        x += pbf.readSVarint();
        y += pbf.readSVarint();

        if (cmd === 1) { // moveTo
          if (coordsLen > currentEnd) {
            ends.push(coordsLen);
            currentEnd = coordsLen;
          }
        }

        flatCoordinates.push(x, y);
        coordsLen += 2;

      } else if (cmd === 7) {

        if (coordsLen > currentEnd) {
          // close polygon
          flatCoordinates.push(
            flatCoordinates[currentEnd], flatCoordinates[currentEnd + 1]);
          coordsLen += 2;
        }

      } else {
        assert(false, 59); // Invalid command found in the PBF
      }
    }

    if (coordsLen > currentEnd) {
      ends.push(coordsLen);
      currentEnd = coordsLen;
    }

  }

  /**
   * @private
   * @param {Object} pbf PBF
   * @param {Object} rawFeature Raw Mapbox feature.
   * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
   * @return {module:ol/Feature|module:ol/render/Feature} Feature.
   */
  createFeature_(pbf, rawFeature, opt_options) {
    const type = rawFeature.type;
    if (type === 0) {
      return null;
    }

    let feature;
    const id = rawFeature.id;
    const values = rawFeature.properties;
    values[this.layerName_] = rawFeature.layer.name;

    const flatCoordinates = [];
    const ends = [];
    this.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends);

    const geometryType = getGeometryType(type, ends.length);

    if (this.featureClass_ === RenderFeature) {
      feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id);
    } else {
      let geom;
      if (geometryType == GeometryType.POLYGON) {
        const endss = [];
        let offset = 0;
        let prevEndIndex = 0;
        for (let i = 0, ii = ends.length; i < ii; ++i) {
          const end = ends[i];
          if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
            endss.push(ends.slice(prevEndIndex, i));
            prevEndIndex = i;
          }
          offset = end;
        }
        if (endss.length > 1) {
          geom = new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss);
        } else {
          geom = new Polygon(flatCoordinates, GeometryLayout.XY, ends);
        }
      } else {
        geom = geometryType === GeometryType.POINT ? new Point(flatCoordinates, GeometryLayout.XY) :
          geometryType === GeometryType.LINE_STRING ? new LineString(flatCoordinates, GeometryLayout.XY) :
            geometryType === GeometryType.POLYGON ? new Polygon(flatCoordinates, GeometryLayout.XY, ends) :
              geometryType === GeometryType.MULTI_POINT ? new MultiPoint(flatCoordinates, GeometryLayout.XY) :
                geometryType === GeometryType.MULTI_LINE_STRING ? new MultiLineString(flatCoordinates, GeometryLayout.XY, ends) :
                  null;
      }
      feature = new this.featureClass_();
      if (this.geometryName_) {
        feature.setGeometryName(this.geometryName_);
      }
      const geometry = transformWithOptions(geom, false, this.adaptOptions(opt_options));
      feature.setGeometry(geometry);
      feature.setId(id);
      feature.setProperties(values);
    }

    return feature;
  }

  /**
   * @inheritDoc
   * @api
   */
  getLastExtent() {
    return this.extent_;
  }

  /**
   * @inheritDoc
   */
  getType() {
    return FormatType.ARRAY_BUFFER;
  }

  /**
   * @inheritDoc
   * @api
   */
  readFeatures(source, opt_options) {
    const layers = this.layers_;

    const pbf = new PBF(/** @type {ArrayBuffer} */ (source));
    const pbfLayers = pbf.readFields(layersPBFReader, {});
    /** @type {Array.<module:ol/Feature|module:ol/render/Feature>} */
    const features = [];
    for (const name in pbfLayers) {
      if (layers && layers.indexOf(name) == -1) {
        continue;
      }
      const pbfLayer = pbfLayers[name];

      for (let i = 0, ii = pbfLayer.length; i < ii; ++i) {
        const rawFeature = readRawFeature(pbf, pbfLayer, i);
        features.push(this.createFeature_(pbf, rawFeature));
      }
      this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null;
    }

    return features;
  }

  /**
   * @inheritDoc
   * @api
   */
  readProjection(source) {
    return this.dataProjection;
  }

  /**
   * Sets the layers that features will be read from.
   * @param {Array.<string>} layers Layers.
   * @api
   */
  setLayers(layers) {
    this.layers_ = layers;
  }

  /**
   * Not implemented.
   * @override
   */
  readFeature() {}

  /**
   * Not implemented.
   * @override
   */
  readGeometry() {}

  /**
   * Not implemented.
   * @override
   */
  writeFeature() {}

  /**
   * Not implemented.
   * @override
   */
  writeGeometry() {}

  /**
   * Not implemented.
   * @override
   */
  writeFeatures() {}
}

inherits(MVT, FeatureFormat);

/**
 * Reader callback for parsing layers.
 * @param {number} tag The tag.
 * @param {Object} layers The layers object.
 * @param {Object} pbf The PBF.
 */
function layersPBFReader(tag, layers, pbf) {
  if (tag === 3) {
    const layer = {
      keys: [],
      values: [],
      features: []
    };
    const end = pbf.readVarint() + pbf.pos;
    pbf.readFields(layerPBFReader, layer, end);
    layer.length = layer.features.length;
    if (layer.length) {
      layers[layer.name] = layer;
    }
  }
}

/**
 * Reader callback for parsing layer.
 * @param {number} tag The tag.
 * @param {Object} layer The layer object.
 * @param {Object} pbf The PBF.
 */
function layerPBFReader(tag, layer, pbf) {
  if (tag === 15) {
    layer.version = pbf.readVarint();
  } else if (tag === 1) {
    layer.name = pbf.readString();
  } else if (tag === 5) {
    layer.extent = pbf.readVarint();
  } else if (tag === 2) {
    layer.features.push(pbf.pos);
  } else if (tag === 3) {
    layer.keys.push(pbf.readString());
  } else if (tag === 4) {
    let value = null;
    const end = pbf.readVarint() + pbf.pos;
    while (pbf.pos < end) {
      tag = pbf.readVarint() >> 3;
      value = tag === 1 ? pbf.readString() :
        tag === 2 ? pbf.readFloat() :
          tag === 3 ? pbf.readDouble() :
            tag === 4 ? pbf.readVarint64() :
              tag === 5 ? pbf.readVarint() :
                tag === 6 ? pbf.readSVarint() :
                  tag === 7 ? pbf.readBoolean() : null;
    }
    layer.values.push(value);
  }
}

/**
 * Reader callback for parsing feature.
 * @param {number} tag The tag.
 * @param {Object} feature The feature object.
 * @param {Object} pbf The PBF.
 */
function featurePBFReader(tag, feature, pbf) {
  if (tag == 1) {
    feature.id = pbf.readVarint();
  } else if (tag == 2) {
    const end = pbf.readVarint() + pbf.pos;
    while (pbf.pos < end) {
      const key = feature.layer.keys[pbf.readVarint()];
      const value = feature.layer.values[pbf.readVarint()];
      feature.properties[key] = value;
    }
  } else if (tag == 3) {
    feature.type = pbf.readVarint();
  } else if (tag == 4) {
    feature.geometry = pbf.pos;
  }
}

/**
 * Read a raw feature from the pbf offset stored at index `i` in the raw layer.
 * @suppress {missingProperties}
 * @param {Object} pbf PBF.
 * @param {Object} layer Raw layer.
 * @param {number} i Index of the feature in the raw layer's `features` array.
 * @return {Object} Raw feature.
 */
function readRawFeature(pbf, layer, i) {
  pbf.pos = layer.features[i];
  const end = pbf.readVarint() + pbf.pos;

  const feature = {
    layer: layer,
    type: 0,
    properties: {}
  };
  pbf.readFields(featurePBFReader, feature, end);
  return feature;
}

/**
 * @suppress {missingProperties}
 * @param {number} type The raw feature's geometry type
 * @param {number} numEnds Number of ends of the flat coordinates of the
 * geometry.
 * @return {module:ol/geom/GeometryType} The geometry type.
 */
function getGeometryType(type, numEnds) {
  /** @type {module:ol/geom/GeometryType} */
  let geometryType;
  if (type === 1) {
    geometryType = numEnds === 1 ?
      GeometryType.POINT : GeometryType.MULTI_POINT;
  } else if (type === 2) {
    geometryType = numEnds === 1 ?
      GeometryType.LINE_STRING :
      GeometryType.MULTI_LINE_STRING;
  } else if (type === 3) {
    geometryType = GeometryType.POLYGON;
    // MultiPolygon not relevant for rendering - winding order determines
    // outer rings of polygons.
  }
  return geometryType;
}

export default MVT;
<commit_msg>Use extends and super for format/MVT<commit_after>/**
 * @module ol/format/MVT
 */
//FIXME Implement projection handling

import {assert} from '../asserts.js';
import PBF from 'pbf';
import FeatureFormat, {transformWithOptions} from '../format/Feature.js';
import FormatType from '../format/FormatType.js';
import GeometryLayout from '../geom/GeometryLayout.js';
import GeometryType from '../geom/GeometryType.js';
import LineString from '../geom/LineString.js';
import MultiLineString from '../geom/MultiLineString.js';
import MultiPoint from '../geom/MultiPoint.js';
import MultiPolygon from '../geom/MultiPolygon.js';
import Point from '../geom/Point.js';
import Polygon from '../geom/Polygon.js';
import {linearRingIsClockwise} from '../geom/flat/orient.js';
import Projection from '../proj/Projection.js';
import Units from '../proj/Units.js';
import RenderFeature from '../render/Feature.js';

/**
 * @typedef {Object} Options
 * @property {function((module:ol/geom/Geometry|Object.<string,*>)=)|function(module:ol/geom/GeometryType,Array.<number>,(Array.<number>|Array.<Array.<number>>),Object.<string,*>,number)} [featureClass]
 * Class for features returned by {@link module:ol/format/MVT#readFeatures}. Set to
 * {@link module:ol/Feature~Feature} to get full editing and geometry support at the cost of
 * decreased rendering performance. The default is {@link module:ol/render/Feature~RenderFeature},
 * which is optimized for rendering and hit detection.
 * @property {string} [geometryName='geometry'] Geometry name to use when creating
 * features.
 * @property {string} [layerName='layer'] Name of the feature attribute that
 * holds the layer name.
 * @property {Array.<string>} [layers] Layers to read features from. If not
 * provided, features will be read from all layers.
 */

/**
 * @classdesc
 * Feature format for reading data in the Mapbox MVT format.
 *
 * @param {module:ol/format/MVT~Options=} opt_options Options.
 * @api
 */
class MVT extends FeatureFormat {

  /**
   * @param {module:ol/format/MVT~Options=} opt_options Options.
   */
  constructor(opt_options) {
    super();

    const options = opt_options ? opt_options : {};

    /**
     * @type {module:ol/proj/Projection}
     */
    this.dataProjection = new Projection({
      code: '',
      units: Units.TILE_PIXELS
    });

    /**
     * @private
     * @type {function((module:ol/geom/Geometry|Object.<string,*>)=)|
     *     function(module:ol/geom/GeometryType,Array.<number>,
     *         (Array.<number>|Array.<Array.<number>>),Object.<string,*>,number)}
     */
    this.featureClass_ = options.featureClass ?
      options.featureClass : RenderFeature;

    /**
     * @private
     * @type {string|undefined}
     */
    this.geometryName_ = options.geometryName;

    /**
     * @private
     * @type {string}
     */
    this.layerName_ = options.layerName ? options.layerName : 'layer';

    /**
     * @private
     * @type {Array.<string>}
     */
    this.layers_ = options.layers ? options.layers : null;

    /**
     * @private
     * @type {module:ol/extent~Extent}
     */
    this.extent_ = null;

  }

  /**
   * Read the raw geometry from the pbf offset stored in a raw feature's geometry
   * property.
   * @suppress {missingProperties}
   * @param {Object} pbf PBF.
   * @param {Object} feature Raw feature.
   * @param {Array.<number>} flatCoordinates Array to store flat coordinates in.
   * @param {Array.<number>} ends Array to store ends in.
   * @private
   */
  readRawGeometry_(pbf, feature, flatCoordinates, ends) {
    pbf.pos = feature.geometry;

    const end = pbf.readVarint() + pbf.pos;
    let cmd = 1;
    let length = 0;
    let x = 0;
    let y = 0;
    let coordsLen = 0;
    let currentEnd = 0;

    while (pbf.pos < end) {
      if (!length) {
        const cmdLen = pbf.readVarint();
        cmd = cmdLen & 0x7;
        length = cmdLen >> 3;
      }

      length--;

      if (cmd === 1 || cmd === 2) {
        x += pbf.readSVarint();
        y += pbf.readSVarint();

        if (cmd === 1) { // moveTo
          if (coordsLen > currentEnd) {
            ends.push(coordsLen);
            currentEnd = coordsLen;
          }
        }

        flatCoordinates.push(x, y);
        coordsLen += 2;

      } else if (cmd === 7) {

        if (coordsLen > currentEnd) {
          // close polygon
          flatCoordinates.push(
            flatCoordinates[currentEnd], flatCoordinates[currentEnd + 1]);
          coordsLen += 2;
        }

      } else {
        assert(false, 59); // Invalid command found in the PBF
      }
    }

    if (coordsLen > currentEnd) {
      ends.push(coordsLen);
      currentEnd = coordsLen;
    }

  }

  /**
   * @private
   * @param {Object} pbf PBF
   * @param {Object} rawFeature Raw Mapbox feature.
   * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
   * @return {module:ol/Feature|module:ol/render/Feature} Feature.
   */
  createFeature_(pbf, rawFeature, opt_options) {
    const type = rawFeature.type;
    if (type === 0) {
      return null;
    }

    let feature;
    const id = rawFeature.id;
    const values = rawFeature.properties;
    values[this.layerName_] = rawFeature.layer.name;

    const flatCoordinates = [];
    const ends = [];
    this.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends);

    const geometryType = getGeometryType(type, ends.length);

    if (this.featureClass_ === RenderFeature) {
      feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id);
    } else {
      let geom;
      if (geometryType == GeometryType.POLYGON) {
        const endss = [];
        let offset = 0;
        let prevEndIndex = 0;
        for (let i = 0, ii = ends.length; i < ii; ++i) {
          const end = ends[i];
          if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
            endss.push(ends.slice(prevEndIndex, i));
            prevEndIndex = i;
          }
          offset = end;
        }
        if (endss.length > 1) {
          geom = new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss);
        } else {
          geom = new Polygon(flatCoordinates, GeometryLayout.XY, ends);
        }
      } else {
        geom = geometryType === GeometryType.POINT ? new Point(flatCoordinates, GeometryLayout.XY) :
          geometryType === GeometryType.LINE_STRING ? new LineString(flatCoordinates, GeometryLayout.XY) :
            geometryType === GeometryType.POLYGON ? new Polygon(flatCoordinates, GeometryLayout.XY, ends) :
              geometryType === GeometryType.MULTI_POINT ? new MultiPoint(flatCoordinates, GeometryLayout.XY) :
                geometryType === GeometryType.MULTI_LINE_STRING ? new MultiLineString(flatCoordinates, GeometryLayout.XY, ends) :
                  null;
      }
      feature = new this.featureClass_();
      if (this.geometryName_) {
        feature.setGeometryName(this.geometryName_);
      }
      const geometry = transformWithOptions(geom, false, this.adaptOptions(opt_options));
      feature.setGeometry(geometry);
      feature.setId(id);
      feature.setProperties(values);
    }

    return feature;
  }

  /**
   * @inheritDoc
   * @api
   */
  getLastExtent() {
    return this.extent_;
  }

  /**
   * @inheritDoc
   */
  getType() {
    return FormatType.ARRAY_BUFFER;
  }

  /**
   * @inheritDoc
   * @api
   */
  readFeatures(source, opt_options) {
    const layers = this.layers_;

    const pbf = new PBF(/** @type {ArrayBuffer} */ (source));
    const pbfLayers = pbf.readFields(layersPBFReader, {});
    /** @type {Array.<module:ol/Feature|module:ol/render/Feature>} */
    const features = [];
    for (const name in pbfLayers) {
      if (layers && layers.indexOf(name) == -1) {
        continue;
      }
      const pbfLayer = pbfLayers[name];

      for (let i = 0, ii = pbfLayer.length; i < ii; ++i) {
        const rawFeature = readRawFeature(pbf, pbfLayer, i);
        features.push(this.createFeature_(pbf, rawFeature));
      }
      this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null;
    }

    return features;
  }

  /**
   * @inheritDoc
   * @api
   */
  readProjection(source) {
    return this.dataProjection;
  }

  /**
   * Sets the layers that features will be read from.
   * @param {Array.<string>} layers Layers.
   * @api
   */
  setLayers(layers) {
    this.layers_ = layers;
  }

  /**
   * Not implemented.
   * @override
   */
  readFeature() {}

  /**
   * Not implemented.
   * @override
   */
  readGeometry() {}

  /**
   * Not implemented.
   * @override
   */
  writeFeature() {}

  /**
   * Not implemented.
   * @override
   */
  writeGeometry() {}

  /**
   * Not implemented.
   * @override
   */
  writeFeatures() {}
}

/**
 * Reader callback for parsing layers.
 * @param {number} tag The tag.
 * @param {Object} layers The layers object.
 * @param {Object} pbf The PBF.
 */
function layersPBFReader(tag, layers, pbf) {
  if (tag === 3) {
    const layer = {
      keys: [],
      values: [],
      features: []
    };
    const end = pbf.readVarint() + pbf.pos;
    pbf.readFields(layerPBFReader, layer, end);
    layer.length = layer.features.length;
    if (layer.length) {
      layers[layer.name] = layer;
    }
  }
}

/**
 * Reader callback for parsing layer.
 * @param {number} tag The tag.
 * @param {Object} layer The layer object.
 * @param {Object} pbf The PBF.
 */
function layerPBFReader(tag, layer, pbf) {
  if (tag === 15) {
    layer.version = pbf.readVarint();
  } else if (tag === 1) {
    layer.name = pbf.readString();
  } else if (tag === 5) {
    layer.extent = pbf.readVarint();
  } else if (tag === 2) {
    layer.features.push(pbf.pos);
  } else if (tag === 3) {
    layer.keys.push(pbf.readString());
  } else if (tag === 4) {
    let value = null;
    const end = pbf.readVarint() + pbf.pos;
    while (pbf.pos < end) {
      tag = pbf.readVarint() >> 3;
      value = tag === 1 ? pbf.readString() :
        tag === 2 ? pbf.readFloat() :
          tag === 3 ? pbf.readDouble() :
            tag === 4 ? pbf.readVarint64() :
              tag === 5 ? pbf.readVarint() :
                tag === 6 ? pbf.readSVarint() :
                  tag === 7 ? pbf.readBoolean() : null;
    }
    layer.values.push(value);
  }
}

/**
 * Reader callback for parsing feature.
 * @param {number} tag The tag.
 * @param {Object} feature The feature object.
 * @param {Object} pbf The PBF.
 */
function featurePBFReader(tag, feature, pbf) {
  if (tag == 1) {
    feature.id = pbf.readVarint();
  } else if (tag == 2) {
    const end = pbf.readVarint() + pbf.pos;
    while (pbf.pos < end) {
      const key = feature.layer.keys[pbf.readVarint()];
      const value = feature.layer.values[pbf.readVarint()];
      feature.properties[key] = value;
    }
  } else if (tag == 3) {
    feature.type = pbf.readVarint();
  } else if (tag == 4) {
    feature.geometry = pbf.pos;
  }
}

/**
 * Read a raw feature from the pbf offset stored at index `i` in the raw layer.
 * @suppress {missingProperties}
 * @param {Object} pbf PBF.
 * @param {Object} layer Raw layer.
 * @param {number} i Index of the feature in the raw layer's `features` array.
 * @return {Object} Raw feature.
 */
function readRawFeature(pbf, layer, i) {
  pbf.pos = layer.features[i];
  const end = pbf.readVarint() + pbf.pos;

  const feature = {
    layer: layer,
    type: 0,
    properties: {}
  };
  pbf.readFields(featurePBFReader, feature, end);
  return feature;
}

/**
 * @suppress {missingProperties}
 * @param {number} type The raw feature's geometry type
 * @param {number} numEnds Number of ends of the flat coordinates of the
 * geometry.
 * @return {module:ol/geom/GeometryType} The geometry type.
 */
function getGeometryType(type, numEnds) {
  /** @type {module:ol/geom/GeometryType} */
  let geometryType;
  if (type === 1) {
    geometryType = numEnds === 1 ?
      GeometryType.POINT : GeometryType.MULTI_POINT;
  } else if (type === 2) {
    geometryType = numEnds === 1 ?
      GeometryType.LINE_STRING :
      GeometryType.MULTI_LINE_STRING;
  } else if (type === 3) {
    geometryType = GeometryType.POLYGON;
    // MultiPolygon not relevant for rendering - winding order determines
    // outer rings of polygons.
  }
  return geometryType;
}

export default MVT;
<|endoftext|>


<commit_before>"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = require("@angular/core");
var DataBindingComponent = (function () {
    function DataBindingComponent() {
        this.url = "http://www.google.com.br";
        this.target = "_blank";
    }
    return DataBindingComponent;
}());
DataBindingComponent = __decorate([
    core_1.Component({
        moduleId: module.id,
        selector: 'data-binding',
        templateUrl: 'data-binding.component.html',
    }),
    __metadata("design:paramtypes", [])
], DataBindingComponent);
exports.DataBindingComponent = DataBindingComponent;
//# sourceMappingURL=data-binding.component.js.map<commit_msg>Adjustment<commit_after>"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = require('@angular/core');
var DataBindingComponent = (function () {
    function DataBindingComponent() {
        this.url = "http://www.google.com.br";
        this.target = "_blank";
    }
    DataBindingComponent = __decorate([
        core_1.Component({
            moduleId: module.id,
            selector: 'data-binding',
            templateUrl: 'data-binding.component.html',
        }),
        __metadata('design:paramtypes', [])
    ], DataBindingComponent);
    return DataBindingComponent;
}());
exports.DataBindingComponent = DataBindingComponent;
//# sourceMappingURL=data-binding.component.js.map<|endoftext|>