<commit_before>/**
 * View: LayerListView
 *
 * List of all layers on a project.
 */
var LayerListView = Backbone.View.extend({
    id: 'LayerListView',
    className: 'view',
    initialize: function(options) {
        _.bindAll(this, 'render', 'sortUpdate');
        this.project = options.project;
        this.collection.bind('add', this.render);
        this.collection.bind('remove', this.render);
        this.render();
    },
    render: function() {
        // Render wrapper if not present.
        if ($(this.el).has('ul').length === 0) {
            $(this.el).html(ich.LayerListView());
            $('ul', this.el).sortable({
                axis: 'y',
                handle: 'div.handle',
                containment: 'parent'
            });
        }

        // Add row view for each layer.
        var that = this;
        this.collection.each(function(layer) {
            if (!layer.view) {
                layer.view = new LayerRowView({
                    project: that.project,
                    model: layer,
                    list: that
                });
                $('ul', that.el).prepend(layer.view.el);
            }
        });

        // Refresh sortable to recognize new layers.
        $('ul', this.el).sortable('refresh');
        return this;
    },
    events: {
        'click .add': 'add',
        'sortupdate ul': 'sortUpdate'
    },
    add: function() {
        new LayerPopupView({
            collection: this.collection,
            model: new Layer,
            add: true
        });
        return false;
    },
    sortUpdate: function(e, ui) {
        var rows = this.$('ul li');
        var newCollection = [];
        this.collection.each(function(model) {
            var index = $.inArray(model.view.el, rows);
            newCollection[index] = model;
        });
        this.collection.models = newCollection.reverse();
        this.collection.trigger('change');
    }
});

/**
 * View: LayerRowView
 *
 * Single layer row in a LayerListView.
 */
var LayerRowView = Backbone.View.extend({
    tagName: 'li',
    className: 'clearfix',
    initialize: function (options) {
        _.bindAll(this, 'render', 'edit', 'inspect', 'del');
        this.model.bind('change', this.render);
        this.project = options.project;
        this.list = options.list;
        this.render();
    },
    render: function () {
        var name = [];
        name.push('#' + this.model.get('id'));
        if (this.model.get('class')) {
            name = name.concat(this.model.get('class').split(' '));
        }
        $(this.el).html(ich.LayerRowView({
            name: name.join('.'),
            geometry: this.model.get('geometry'),
            geometry_raster: this.model.get('geometry') === 'raster'
        }));
        return this;
    },
    events: {
        'click .delete': 'del',
        'click .inspect': 'inspect',
        'click .edit': 'edit'
    },
    edit: function() {
        new LayerPopupView({
            collection: this.collection,
            model: this.model,
            add: false
        });
        return false;
    },
    inspect: function() {
        new DatasourceView({
            model: new Datasource({
                id: this.model.id,
                url: this.model.get('Datasource').file
            })
        });
        return false;
    },
    del: function() {
        window.app.loading();
        if (confirm('Are you sure you want to delete this layer?')) {
            // Tipsy adds the tooltips to the document root,
            // so we need to remove these elements, otherwise we
            // are left with a zombie 'delete' tooltip.
            $('.tipsy').remove();

            this.list.collection.remove(this.model);
            this.remove();
            window.app.done();
        }
        else {
            window.app.done();
        }
        return false;
    }
});

/**
 * View: LayerPopupView
 *
 * Popup form for adding a new layer.
 */
var LayerPopupView = PopupView.extend({
    SRS: {
        '900913': '+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',
        'WGS84': '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
    },
    events: _.extend({
        'click input.submit': 'submit',
        'click a.assets': 'assets',
        'change select#srs-name': 'selectSRS'
    }, PopupView.prototype.events),
    initialize: function(params) {
        _.bindAll(this, 'render', 'submit', 'assets', 'getSRSName', 'selectSRS', 'showError');
        this.model = this.options.model;
        this.options.title = this.options.add ? 'Add layer' : 'Edit layer';

        var object = {
            'id': this.model.id,
            'class': this.model.get('class'),
            'datasource_file': this.model.get('Datasource') ?
                this.model.get('Datasource').file : '',
            'srs': this.model.get('srs')
        };
        object['srs_name_' + this.getSRSName(this.model.get('srs'))] = true;
        this.options.content = ich.LayerPopupView(object, true);
        this.render();
    },
    submit: function() {
        var that = this;
        var datasource = new Datasource({
            id: $('input#id', this.el).val(),
            url: $('input#file', this.el).val()
        });
        this.loading('Loading datasource');
        datasource.fetch({
            success: function() {
                that.done();
                var success = that.model.set(
                    {
                        'id': $('input#id', that.el).val(),
                        'name': $('input#id', that.el).val(),
                        'srs': (datasource.get('ds_type') === 'gdal')
                            ? that.SRS['900913']
                            : $('input#srs', that.el).val(),
                        'class': $('input#class', that.el).val(),
                        'geometry': datasource.get('geometry_type'),
                        'Datasource': _.extend(
                            { 'file': $('input#file', that.el).val() },
                            datasource.get('ds_options')
                        )
                    },
                    { 'error': that.showError }
                );
                if (success) {
                    that.options.add && that.collection.add(that.model);
                    that.remove();
                }
            },
            error: that.showError
        });
        return false;
    },
    assets: function() {
        (new LibraryList()).fetch({
            success: function(collection) {
                new LibraryListPopupView({
                    collection: collection,
                    target: $('input#file')
                });
            }
        });
        return false;
    },
    showError: function(model, error) {
        this.done();
        window.app.message('Error', error);
    },
    getSRSName: function(srs) {
        for (name in this.SRS) {
            if (this.SRS[name] === srs) {
                return name;
            }
        }
        return srs ? 'custom' : 'autodetect';
    },
    selectSRS: function() {
        var name = $('select#srs-name', this.el).val();
        if (name === 'custom') {
            $('.srs', this.el).show();
        }
        else {
            $('input#srs', this.el).val(this.SRS[name]);
            $('.srs', this.el).hide();
        }
    }
});

/**
 * View: LayerDrawerView.
 *
 * Drawer view for inspecting layer fields.
 */
var DatasourceView = DrawerView.extend({
    className: 'drawer',
    events: _.extend({
        'click .showall': 'deferredRender'
    }, DrawerView.prototype.events),
    initialize: function (options) {
        options.title = this.model.id;
        options.content = '';
        DrawerView.prototype.initialize.call(this, options);

        _.bindAll(this, 'deferredRender', 'loadFields');
        var that = this;
        this.bind('render', function() {
            that.model.fetch({
                'success': that.loadFields,
                'error': that.loadFields
            });
        });
        this.features = [];
        this.deferredFeatures = [];
    },
    loadFields: function () {
        var object = { fields: [] };
        for (var fieldId in this.model.get('fields')) {
            var field = this.model.get('fields')[fieldId];
            field.name = fieldId;
            if (field.type == 'Number') {
                field.numeric = true;
            }
            field.tooltip = ich.DatasourceToolTip(field, true);
            object.fields.push(field);
        }
        var max_cells = 1000;
        var features = this.model.get('features');
        for (var i = 0; i < features.length; i++) {
            var feature = features[i];
            var featureArray = [];
            for (var j = 0; j < object.fields.length; j++) {
                var field = object.fields[j].name;
                featureArray.push({
                    value: feature[field] ? feature[field] : '',
                    type: object.fields[j].type
                });
            }
            if (i * object.fields.length <= max_cells) {
                this.features.push({ values: featureArray });
            }
            else {
                this.deferredFeatures.push({ values: featureArray });
            }
        }
        object.rows = ich.DatasourceRowsView({features: this.features}, true);
        this.$('.drawer-content').html(ich.DatasourceView(object, true));
        if (this.deferredFeatures.length) {
            this.$('.drawer-content').append(ich.DatasourceViewAllRowsButtonView({deferredCount: this.deferredFeatures.length}));
        }
        return this;
    },
    deferredRender: function() {
        this.$('.drawer-content .showall').remove();
        var rows = ich.DatasourceRowsView({features: this.deferredFeatures});
        this.$('table.features tbody').append(rows);
        return false;
    }
});

<commit_msg>Check datasource validation as well as layers.<commit_after>/**
 * View: LayerListView
 *
 * List of all layers on a project.
 */
var LayerListView = Backbone.View.extend({
    id: 'LayerListView',
    className: 'view',
    initialize: function(options) {
        _.bindAll(this, 'render', 'sortUpdate');
        this.project = options.project;
        this.collection.bind('add', this.render);
        this.collection.bind('remove', this.render);
        this.render();
    },
    render: function() {
        // Render wrapper if not present.
        if ($(this.el).has('ul').length === 0) {
            $(this.el).html(ich.LayerListView());
            $('ul', this.el).sortable({
                axis: 'y',
                handle: 'div.handle',
                containment: 'parent'
            });
        }

        // Add row view for each layer.
        var that = this;
        this.collection.each(function(layer) {
            if (!layer.view) {
                layer.view = new LayerRowView({
                    project: that.project,
                    model: layer,
                    list: that
                });
                $('ul', that.el).prepend(layer.view.el);
            }
        });

        // Refresh sortable to recognize new layers.
        $('ul', this.el).sortable('refresh');
        return this;
    },
    events: {
        'click .add': 'add',
        'sortupdate ul': 'sortUpdate'
    },
    add: function() {
        new LayerPopupView({
            collection: this.collection,
            model: new Layer,
            add: true
        });
        return false;
    },
    sortUpdate: function(e, ui) {
        var rows = this.$('ul li');
        var newCollection = [];
        this.collection.each(function(model) {
            var index = $.inArray(model.view.el, rows);
            newCollection[index] = model;
        });
        this.collection.models = newCollection.reverse();
        this.collection.trigger('change');
    }
});

/**
 * View: LayerRowView
 *
 * Single layer row in a LayerListView.
 */
var LayerRowView = Backbone.View.extend({
    tagName: 'li',
    className: 'clearfix',
    initialize: function (options) {
        _.bindAll(this, 'render', 'edit', 'inspect', 'del');
        this.model.bind('change', this.render);
        this.project = options.project;
        this.list = options.list;
        this.render();
    },
    render: function () {
        var name = [];
        name.push('#' + this.model.get('id'));
        if (this.model.get('class')) {
            name = name.concat(this.model.get('class').split(' '));
        }
        $(this.el).html(ich.LayerRowView({
            name: name.join('.'),
            geometry: this.model.get('geometry'),
            geometry_raster: this.model.get('geometry') === 'raster'
        }));
        return this;
    },
    events: {
        'click .delete': 'del',
        'click .inspect': 'inspect',
        'click .edit': 'edit'
    },
    edit: function() {
        new LayerPopupView({
            collection: this.collection,
            model: this.model,
            add: false
        });
        return false;
    },
    inspect: function() {
        new DatasourceView({
            model: new Datasource({
                id: this.model.id,
                url: this.model.get('Datasource').file
            })
        });
        return false;
    },
    del: function() {
        window.app.loading();
        if (confirm('Are you sure you want to delete this layer?')) {
            // Tipsy adds the tooltips to the document root,
            // so we need to remove these elements, otherwise we
            // are left with a zombie 'delete' tooltip.
            $('.tipsy').remove();

            this.list.collection.remove(this.model);
            this.remove();
            window.app.done();
        }
        else {
            window.app.done();
        }
        return false;
    }
});

/**
 * View: LayerPopupView
 *
 * Popup form for adding a new layer.
 */
var LayerPopupView = PopupView.extend({
    SRS: {
        '900913': '+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',
        'WGS84': '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
    },
    events: _.extend({
        'click input.submit': 'submit',
        'click a.assets': 'assets',
        'change select#srs-name': 'selectSRS'
    }, PopupView.prototype.events),
    initialize: function(params) {
        _.bindAll(this, 'render', 'submit', 'assets', 'getSRSName', 'selectSRS', 'showError');
        this.model = this.options.model;
        this.options.title = this.options.add ? 'Add layer' : 'Edit layer';

        var object = {
            'id': this.model.id,
            'class': this.model.get('class'),
            'datasource_file': this.model.get('Datasource') ?
                this.model.get('Datasource').file : '',
            'srs': this.model.get('srs')
        };
        object['srs_name_' + this.getSRSName(this.model.get('srs'))] = true;
        this.options.content = ich.LayerPopupView(object, true);
        this.render();
    },
    submit: function() {
        var that = this;
        var datasource = new Datasource();
        var success = datasource.set({
            id: $('input#id', this.el).val(),
            url: $('input#file', this.el).val()
        }, { error: that.showError });
        if (success) {
            this.loading('Loading datasource');
            datasource.fetch({
                success: function() {
                    that.done();
                    var success = that.model.set(
                        {
                            'id': $('input#id', that.el).val(),
                            'name': $('input#id', that.el).val(),
                            'srs': (datasource.get('ds_type') === 'gdal')
                                ? that.SRS['900913']
                                : $('input#srs', that.el).val(),
                            'class': $('input#class', that.el).val(),
                            'geometry': datasource.get('geometry_type'),
                            'Datasource': _.extend(
                                { 'file': $('input#file', that.el).val() },
                                datasource.get('ds_options')
                            )
                        },
                        { 'error': that.showError }
                    );
                    if (success) {
                        that.options.add && that.collection.add(that.model);
                        that.remove();
                    }
                },
                error: that.showError
            });
        }
        return false;
    },
    assets: function() {
        (new LibraryList()).fetch({
            success: function(collection) {
                new LibraryListPopupView({
                    collection: collection,
                    target: $('input#file')
                });
            }
        });
        return false;
    },
    showError: function(model, error) {
        this.done();
        window.app.message('Error', error);
    },
    getSRSName: function(srs) {
        for (name in this.SRS) {
            if (this.SRS[name] === srs) {
                return name;
            }
        }
        return srs ? 'custom' : 'autodetect';
    },
    selectSRS: function() {
        var name = $('select#srs-name', this.el).val();
        if (name === 'custom') {
            $('.srs', this.el).show();
        }
        else {
            $('input#srs', this.el).val(this.SRS[name]);
            $('.srs', this.el).hide();
        }
    }
});

/**
 * View: LayerDrawerView.
 *
 * Drawer view for inspecting layer fields.
 */
var DatasourceView = DrawerView.extend({
    className: 'drawer',
    events: _.extend({
        'click .showall': 'deferredRender'
    }, DrawerView.prototype.events),
    initialize: function (options) {
        options.title = this.model.id;
        options.content = '';
        DrawerView.prototype.initialize.call(this, options);

        _.bindAll(this, 'deferredRender', 'loadFields');
        var that = this;
        this.bind('render', function() {
            that.model.fetch({
                'success': that.loadFields,
                'error': that.loadFields
            });
        });
        this.features = [];
        this.deferredFeatures = [];
    },
    loadFields: function () {
        var object = { fields: [] };
        for (var fieldId in this.model.get('fields')) {
            var field = this.model.get('fields')[fieldId];
            field.name = fieldId;
            if (field.type == 'Number') {
                field.numeric = true;
            }
            field.tooltip = ich.DatasourceToolTip(field, true);
            object.fields.push(field);
        }
        var max_cells = 1000;
        var features = this.model.get('features');
        for (var i = 0; i < features.length; i++) {
            var feature = features[i];
            var featureArray = [];
            for (var j = 0; j < object.fields.length; j++) {
                var field = object.fields[j].name;
                featureArray.push({
                    value: feature[field] ? feature[field] : '',
                    type: object.fields[j].type
                });
            }
            if (i * object.fields.length <= max_cells) {
                this.features.push({ values: featureArray });
            }
            else {
                this.deferredFeatures.push({ values: featureArray });
            }
        }
        object.rows = ich.DatasourceRowsView({features: this.features}, true);
        this.$('.drawer-content').html(ich.DatasourceView(object, true));
        if (this.deferredFeatures.length) {
            this.$('.drawer-content').append(ich.DatasourceViewAllRowsButtonView({deferredCount: this.deferredFeatures.length}));
        }
        return this;
    },
    deferredRender: function() {
        this.$('.drawer-content .showall').remove();
        var rows = ich.DatasourceRowsView({features: this.deferredFeatures});
        this.$('table.features tbody').append(rows);
        return false;
    }
});

<|endoftext|>


<commit_before>'use strict';

define([
	'react',
	'react-router',
	'reflux',
	'app/actions/build',
	'app/stores/build',
	'app/components/terminal/terminal',
	'app/components/buildSidebar/index',
	'templates/app/components/builds/view',
	'app/components/common/index'
], function(
	React, Router, Reflux, BuildActions, buildStore, TerminalComponent,
	BuildSidebar, template, CommonComponents
) {
	template = template.locals({
		DateTime: CommonComponents.DateTime,
		Duration: CommonComponents.Duration,
		Scm: CommonComponents.Scm,
		Terminal: TerminalComponent,
		Link: Router.Link,
		BuildSidebar: BuildSidebar
	});

	var Component = React.createClass({
		mixins: [Reflux.ListenerMixin],
		statics: {
			willTransitionTo: function(transition, params, query) {
				BuildActions.read(Number(params.id));
			}
		},
		componentDidMount: function() {
			this.listenTo(buildStore, this.updateBuild);
		},
		updateBuild: function(build) {
			if (build) {
				BuildActions.readAll({projectName: build.project.name});
			}
			this.setState({build: build});
		},
		render: template,
		getInitialState: function() {
			return {
				build: null,
				showConsole: false
			};
		},
		toggleConsole: function() {
			var consoleState = !this.state.showConsole;
			if (consoleState) {
				BuildActions.readTerminalOutput(this.state.build);
			}
			this.setState({showConsole: consoleState});
		}
	});

	return Component;
});
<commit_msg>reset show console button status on build change<commit_after>'use strict';

define([
	'react',
	'react-router',
	'reflux',
	'app/actions/build',
	'app/stores/build',
	'app/components/terminal/terminal',
	'app/components/buildSidebar/index',
	'templates/app/components/builds/view',
	'app/components/common/index'
], function(
	React, Router, Reflux, BuildActions, buildStore, TerminalComponent,
	BuildSidebar, template, CommonComponents
) {
	template = template.locals({
		DateTime: CommonComponents.DateTime,
		Duration: CommonComponents.Duration,
		Scm: CommonComponents.Scm,
		Terminal: TerminalComponent,
		Link: Router.Link,
		BuildSidebar: BuildSidebar
	});

	var Component = React.createClass({
		mixins: [Reflux.ListenerMixin],
		statics: {
			willTransitionTo: function(transition, params, query) {
				BuildActions.read(Number(params.id));
			}
		},
		componentDidMount: function() {
			this.listenTo(buildStore, this.updateBuild);
		},
		componentWillReceiveProps: function(nextProps) {
			// reset console status when go from build page to another build
			// page (did mount and mount not called in this case)
			if (Number(nextProps.params.id) !== this.state.build.id) {
				this.setState({showConsole: this.getInitialState().showConsole});
			}
		},
		updateBuild: function(build) {
			if (build) {
				BuildActions.readAll({projectName: build.project.name});
			}
			this.setState({build: build});
		},
		render: template,
		getInitialState: function() {
			return {
				build: null,
				showConsole: false
			};
		},
		toggleConsole: function() {
			var consoleState = !this.state.showConsole;
			if (consoleState) {
				BuildActions.readTerminalOutput(this.state.build);
			}
			this.setState({showConsole: consoleState});
		}
	});

	return Component;
});
<|endoftext|>


<commit_before>// jscs:disable jsDoc
'use strict';

var _ = require('underscore'),
	path = require('path'),
	fs = require('fs-extended'),
	gm = require('gm'),
	colors = require('colors'),
	config = require('./config'),
	specs = require('./specs'),
	logger = require('./logger');

/**
 * Creates task to run async
 * @param  {String}   name     spec to use
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, task)
 */
exports.createTask = function createTask(name, opts, callback) {
	var self = this;

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get spec
		self.getSpec(name, cfg, function (err, spec) {

			if (err) {
				return callback(err);
			}

			// pass back task
			callback(null, function (_callback) {
				self.run(spec, cfg, _callback);
			});
		});
	});
};

/**
 * Creates tasks to run async
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, task)
 */
exports.createTasks = function createTasks(opts, callback) {
	var self = this;

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get specs
		self.getSpecs(cfg, function (err, specs) {

			if (err) {
				return callback(err);
			}

			var tasks = {};

			// for each spec
			_.each(specs, function (spec) {

				// push task
				tasks[spec.name] = function (_callback) {
					self.run(spec, cfg, _callback);
				};
			});

			// pass back tasks
			callback(null, tasks);
		});
	});
};

/**
 * Run a task
 * @param  {Object}   specs    initialized specificiation
 * @param  {Object}   cfg      initialized configuration
 * @param  {Function} callback callback(err, output)
 */
exports.run = function run(spec, cfg, callback) {

	// do not output to input
	if (spec.output === cfg.input) {

		// inform CLI
		if (cfg.cli) {
			logger.info('Skipping input as output: ' + spec.output.cyan);
		}

		return callback();
	}

	fs.exists(cfg.input, function (exists) {

		// input doesn't exist
		if (!exists) {
			return callback('Input not found: ' + cfg.input);
		}

		// NOTE: non-sync version didn't work in paralel
		fs.createDirSync(path.dirname(spec.output));

		var im = gm.subClass({
			imageMagick: true
		});

		// read
		var convert = im(cfg.input);

		var flatten = !spec.alpha && !(cfg.nine && spec.ninePath);

		// flatten
		if (flatten) {
			convert = convert.flatten();
		}

		var specRatio = spec.width / spec.height;

		var containWidth, containHeight;

		// nine or fill
		if (!cfg.crop || (cfg.nine && (spec.ninePath || spec.alsoNine))) {

			// only scale down
			if (cfg.inputWidth > spec.width || cfg.inputHeight > spec.height) {

				if (cfg.inputRatio > specRatio) {
					containWidth = spec.width;
					containHeight = spec.width / cfg.inputRatio;
				} else {
					containWidth = spec.height * cfg.inputRatio;
					containHeight = spec.height;
				}

				// resize fitting dimensions (using in() or order will be wrong for fill)
				convert = convert
					.in('-resize').in(spec.width + 'x' + spec.height);

			} else {
				containWidth = cfg.inputWidth;
				containHeight = cfg.inputHeight;
			}

			// nine
			if (cfg.nine && spec.ninePath) {

				// extent with 1px transparent border
				convert = convert
					.borderColor('none')
					.border(1, 1);

				// draw black pixels
				convert = convert
					.fill('black')

					// stretchable area
					.drawPoint(1, 0)
					.drawPoint(containWidth, 0)
					.drawPoint(0, 1)
					.drawPoint(0, containHeight)

					// padding box (required since API 21)
					.drawLine(containWidth + 1, 2, containWidth + 1, containHeight - 1)
					.drawLine(2, containHeight + 1, containWidth - 1, containHeight + 1);

				// fill
			} else {

				// calculate required padding
				var padLeft = Math.floor((spec.width - containWidth) / 2);
				var padTop = Math.floor((spec.height - containHeight) / 2);

				// enlarge canvas using outer pixel to fill (all using in() or order will be wrong)
				convert = convert
					.in('-define')
					.in('distort:viewport=' + spec.width + 'x' + spec.height + '-' + padLeft + '-' + padTop)
					.in('-distort').in('SRT').in('0')
					.in('+repage');
			}

			// crop
		} else {

			var resize = (function () {
				var contentRatio, z, smallerWidth, smallerHeight, factor,
					result = {
						width: spec.width,
						height: spec.height
					};

				// user has given content dimensions
				if (cfg.width && cfg.height) {
					contentRatio = cfg.width / cfg.height;

					// e.g. specs landscape, content is portrait
					if (specRatio > contentRatio) {
						z = spec.height / cfg.height;

						// e.g. specs portrait, content is landscape
					} else {
						z = spec.width / cfg.width;
					}

					// don't scale up
					if (z > 1) {
						logger.warn('Target size is bigger then content: ' + spec.output.cyan);

					} else {
						smallerWidth = Math.round(cfg.inputWidth * z);
						smallerHeight = Math.round(cfg.inputHeight * z);

						// width or height won't cover specs anymore if content needs to fit as well
						if (smallerWidth < spec.width || smallerHeight < spec.height) {
							factor = Math.max(spec.width / smallerWidth, spec.height / smallerHeight);
							logger.warn('For '.white + (spec.width + 'x' + spec.height).cyan + ' to contain '.white + (cfg.width + 'x' + cfg.height).cyan + ' the input needs to be at least: '.white + (Math.ceil(cfg.inputWidth * factor) + 'x' + Math.ceil(cfg.inputHeight * factor)).cyan);

						} else {
							result.width = smallerWidth;
							result.height = smallerHeight;
						}
					}
				}

				return result;

			})();

			// resize covering dimensions
			convert = convert.resize(resize.width, resize.height, '^');

			// crop from center to exact dimensions
			convert = convert
				.gravity('Center')
				.crop(spec.width, spec.height, 0, 0);
		}

		// border radius (http://www.rubblewebs.co.uk/imagemagick/display_example.php?example=69)
		if (spec.type === 'icon' && !flatten && cfg.radius > 0 && _.size(_.difference(spec.platforms, ['ios', 'ipad', 'iphone', 'apple-watch'])) > 0) {
			var radius = Math.ceil((spec.width / 100) * cfg.radius);

			// in
			convert = convert
				.in('-size').in(spec.width + 'x' + spec.height)
				.in('xc:none')
				.in('-fill').in('white')
				.in('-draw').in('roundRectangle 0,0 ' + spec.width + ',' + spec.height + ' ' + radius + ',' + radius);

			// out
			convert = convert
				.compose('SrcIn')
				.out('-composite');
		}

		// annotate
		if (cfg.label) {
			var fontSize = Math.round((containWidth || spec.width) / 11);

			convert = convert
				.fill('#F00')
				.font(path.join(__dirname, 'Calibri.ttf'))
				.fontSize(fontSize);

			if (cfg.nine && spec.ninePath) {
				convert = convert.drawText(1, (containHeight || spec.height) / 2, spec.name);
			} else {
				convert = convert.drawText(0, 0, spec.name);
			}
		}

		// headers
		convert = convert
			.noProfile()
			.units('PixelsPerInch')
			.density(spec.dpi, spec.dpi);

		// rotate
		if (spec.rotate) {
			convert = convert.rotate('white', spec.rotate);
		}

		// remove alpha
		if (flatten) {
			convert = convert.out('-background', 'white', '-alpha', 'off');
		}

		// write
		convert.write(spec.output, function (err) {

			if (err) {
				return callback(err);
			}

			// async feedback for CLI
			if (cfg.cli) {
				logger.info('Generated: ' + spec.output.cyan);
			}

			// pass back output
			callback(null, spec.output);

		});

		// show command
		if (cfg.trace) {
			logger.debug('Executing: ' + convert.args().join(' ').cyan);
		}

	});
};

/**
 * Get a spec
 * @param  {String}   name     spec to get
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, output)
 */
exports.getSpec = function getSpec(name, opts, callback) {

	// not really needed, but for consistency ;)
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get initted spec
		var spec = _initSpec(name, cfg);

		// spec not found
		if (!spec) {
			return callback('Spec not found: ' + name);
		}

		// pass back spec
		callback(null, spec);
	});
};

/**
 * Get specs
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, output)
 */
exports.getSpecs = function getSpecs(opts, callback) {

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		var _specs = {};

		// filter specs
		_.each(specs, function (spec, name) {

			// does not meet type
			if (cfg.type && spec.type !== cfg.type) {
				return;
			}

			// doest not meet orientation
			if (spec.type === 'splash' && spec.orientation && cfg.orientation && cfg.orientation !== spec.orientation) {
				return;
			}

			// does not meet min dpi
			if (spec.dpi && cfg.minDpi && spec.dpi < cfg.minDpi) {
				return;
			}

			// does not meet max dpi
			if (spec.dpi && cfg.maxDpi && spec.dpi > cfg.maxDpi) {
				return;
			}

			// has no localized path
			if (cfg.locale && !spec.localePath) {
				return;
			}

			// has no 9-patch path
			if (cfg.nine && spec.type === 'splash' && _.indexOf(spec.platforms, 'android') !== -1 && !(spec.ninePath || spec.alsoNine)) {
				return;
			}

			// does not meet at least one platform
			if (!_.some(spec.platforms, function (platform) {
					return !!cfg[platform];
				})) {

				return false;
			}

			// push initted spec
			_specs[name] = _initSpec(name, cfg);
		});

		// no specs not found
		if (_.size(_specs) === 0) {
			return callback('No specs found');
		}

		// pass back specs
		callback(null, _specs);
	});
};

function _initSpec(name, cfg) {

	// doesn't exist
	if (!specs[name]) {
		return;
	}

	// cloning
	var spec = _.clone(specs[name]);

	// name
	spec.name = name;

	// default dpi
	spec.dpi = spec.dpi || 72;

	// size to widthxheight
	if (spec.size) {
		spec.width = spec.height = spec.size;
	}

	// fix
	if (cfg.fix) {
		if (spec.widthFix) {
			spec.width = spec.widthFix;
		}
		if (spec.heightFix) {
			spec.height = spec.heightFix;
		}
	}

	// full output path
	var _path;

	if (cfg.nine && spec.ninePath) {
		_path = cfg.locale ? spec.nineLocalePath.replace(':locale:', cfg.locale) : spec.ninePath;

	} else {
		_path = (cfg.locale && spec.localePath) ? spec.localePath.replace(':locale:', cfg.locale) : spec.path;
		_path = _path.replace(':assets:', cfg.assetsDir);
	}

	spec.output = path.join(cfg.outputDir, _path);

	return spec;
}
<commit_msg>Fix red line around statusbar and software buttons on Android 5.x<commit_after>// jscs:disable jsDoc
'use strict';

var _ = require('underscore'),
	path = require('path'),
	fs = require('fs-extended'),
	gm = require('gm'),
	colors = require('colors'),
	config = require('./config'),
	specs = require('./specs'),
	logger = require('./logger');

/**
 * Creates task to run async
 * @param  {String}   name     spec to use
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, task)
 */
exports.createTask = function createTask(name, opts, callback) {
	var self = this;

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get spec
		self.getSpec(name, cfg, function (err, spec) {

			if (err) {
				return callback(err);
			}

			// pass back task
			callback(null, function (_callback) {
				self.run(spec, cfg, _callback);
			});
		});
	});
};

/**
 * Creates tasks to run async
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, task)
 */
exports.createTasks = function createTasks(opts, callback) {
	var self = this;

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get specs
		self.getSpecs(cfg, function (err, specs) {

			if (err) {
				return callback(err);
			}

			var tasks = {};

			// for each spec
			_.each(specs, function (spec) {

				// push task
				tasks[spec.name] = function (_callback) {
					self.run(spec, cfg, _callback);
				};
			});

			// pass back tasks
			callback(null, tasks);
		});
	});
};

/**
 * Run a task
 * @param  {Object}   specs    initialized specificiation
 * @param  {Object}   cfg      initialized configuration
 * @param  {Function} callback callback(err, output)
 */
exports.run = function run(spec, cfg, callback) {

	// do not output to input
	if (spec.output === cfg.input) {

		// inform CLI
		if (cfg.cli) {
			logger.info('Skipping input as output: ' + spec.output.cyan);
		}

		return callback();
	}

	fs.exists(cfg.input, function (exists) {

		// input doesn't exist
		if (!exists) {
			return callback('Input not found: ' + cfg.input);
		}

		// NOTE: non-sync version didn't work in paralel
		fs.createDirSync(path.dirname(spec.output));

		var im = gm.subClass({
			imageMagick: true
		});

		// read
		var convert = im(cfg.input);

		var flatten = !spec.alpha && !(cfg.nine && spec.ninePath);

		// flatten
		if (flatten) {
			convert = convert.flatten();
		}

		var specRatio = spec.width / spec.height;

		var containWidth, containHeight;

		// nine or fill
		if (!cfg.crop || (cfg.nine && (spec.ninePath || spec.alsoNine))) {

			// only scale down
			if (cfg.inputWidth > spec.width || cfg.inputHeight > spec.height) {

				if (cfg.inputRatio > specRatio) {
					containWidth = spec.width;
					containHeight = spec.width / cfg.inputRatio;
				} else {
					containWidth = spec.height * cfg.inputRatio;
					containHeight = spec.height;
				}

				// resize fitting dimensions (using in() or order will be wrong for fill)
				convert = convert
					.in('-resize').in(spec.width + 'x' + spec.height);

			} else {
				containWidth = cfg.inputWidth;
				containHeight = cfg.inputHeight;
			}

			// nine
			if (cfg.nine && spec.ninePath) {

				// extent with 1px transparent border
				convert = convert
					.borderColor('none')
					.border(1, 1);

				// draw black pixels
				convert = convert
					.fill('black')

					// stretchable area
					.drawPoint(1, 0)
					.drawPoint(containWidth, 0)
					.drawPoint(0, 1)
					.drawPoint(0, containHeight)

					// padding box (required since API 21)
					.drawLine(containWidth + 1, 1, containWidth + 1, containHeight)
					.drawLine(1, containHeight + 1, containWidth, containHeight + 1);

				// fill
			} else {

				// calculate required padding
				var padLeft = Math.floor((spec.width - containWidth) / 2);
				var padTop = Math.floor((spec.height - containHeight) / 2);

				// enlarge canvas using outer pixel to fill (all using in() or order will be wrong)
				convert = convert
					.in('-define')
					.in('distort:viewport=' + spec.width + 'x' + spec.height + '-' + padLeft + '-' + padTop)
					.in('-distort').in('SRT').in('0')
					.in('+repage');
			}

			// crop
		} else {

			var resize = (function () {
				var contentRatio, z, smallerWidth, smallerHeight, factor,
					result = {
						width: spec.width,
						height: spec.height
					};

				// user has given content dimensions
				if (cfg.width && cfg.height) {
					contentRatio = cfg.width / cfg.height;

					// e.g. specs landscape, content is portrait
					if (specRatio > contentRatio) {
						z = spec.height / cfg.height;

						// e.g. specs portrait, content is landscape
					} else {
						z = spec.width / cfg.width;
					}

					// don't scale up
					if (z > 1) {
						logger.warn('Target size is bigger then content: ' + spec.output.cyan);

					} else {
						smallerWidth = Math.round(cfg.inputWidth * z);
						smallerHeight = Math.round(cfg.inputHeight * z);

						// width or height won't cover specs anymore if content needs to fit as well
						if (smallerWidth < spec.width || smallerHeight < spec.height) {
							factor = Math.max(spec.width / smallerWidth, spec.height / smallerHeight);
							logger.warn('For '.white + (spec.width + 'x' + spec.height).cyan + ' to contain '.white + (cfg.width + 'x' + cfg.height).cyan + ' the input needs to be at least: '.white + (Math.ceil(cfg.inputWidth * factor) + 'x' + Math.ceil(cfg.inputHeight * factor)).cyan);

						} else {
							result.width = smallerWidth;
							result.height = smallerHeight;
						}
					}
				}

				return result;

			})();

			// resize covering dimensions
			convert = convert.resize(resize.width, resize.height, '^');

			// crop from center to exact dimensions
			convert = convert
				.gravity('Center')
				.crop(spec.width, spec.height, 0, 0);
		}

		// border radius (http://www.rubblewebs.co.uk/imagemagick/display_example.php?example=69)
		if (spec.type === 'icon' && !flatten && cfg.radius > 0 && _.size(_.difference(spec.platforms, ['ios', 'ipad', 'iphone', 'apple-watch'])) > 0) {
			var radius = Math.ceil((spec.width / 100) * cfg.radius);

			// in
			convert = convert
				.in('-size').in(spec.width + 'x' + spec.height)
				.in('xc:none')
				.in('-fill').in('white')
				.in('-draw').in('roundRectangle 0,0 ' + spec.width + ',' + spec.height + ' ' + radius + ',' + radius);

			// out
			convert = convert
				.compose('SrcIn')
				.out('-composite');
		}

		// annotate
		if (cfg.label) {
			var fontSize = Math.round((containWidth || spec.width) / 11);

			convert = convert
				.fill('#F00')
				.font(path.join(__dirname, 'Calibri.ttf'))
				.fontSize(fontSize);

			if (cfg.nine && spec.ninePath) {
				convert = convert.drawText(1, (containHeight || spec.height) / 2, spec.name);
			} else {
				convert = convert.drawText(0, 0, spec.name);
			}
		}

		// headers
		convert = convert
			.noProfile()
			.units('PixelsPerInch')
			.density(spec.dpi, spec.dpi);

		// rotate
		if (spec.rotate) {
			convert = convert.rotate('white', spec.rotate);
		}

		// remove alpha
		if (flatten) {
			convert = convert.out('-background', 'white', '-alpha', 'off');
		}

		// write
		convert.write(spec.output, function (err) {

			if (err) {
				return callback(err);
			}

			// async feedback for CLI
			if (cfg.cli) {
				logger.info('Generated: ' + spec.output.cyan);
			}

			// pass back output
			callback(null, spec.output);

		});

		// show command
		if (cfg.trace) {
			logger.debug('Executing: ' + convert.args().join(' ').cyan);
		}

	});
};

/**
 * Get a spec
 * @param  {String}   name     spec to get
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, output)
 */
exports.getSpec = function getSpec(name, opts, callback) {

	// not really needed, but for consistency ;)
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get initted spec
		var spec = _initSpec(name, cfg);

		// spec not found
		if (!spec) {
			return callback('Spec not found: ' + name);
		}

		// pass back spec
		callback(null, spec);
	});
};

/**
 * Get specs
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, output)
 */
exports.getSpecs = function getSpecs(opts, callback) {

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		var _specs = {};

		// filter specs
		_.each(specs, function (spec, name) {

			// does not meet type
			if (cfg.type && spec.type !== cfg.type) {
				return;
			}

			// doest not meet orientation
			if (spec.type === 'splash' && spec.orientation && cfg.orientation && cfg.orientation !== spec.orientation) {
				return;
			}

			// does not meet min dpi
			if (spec.dpi && cfg.minDpi && spec.dpi < cfg.minDpi) {
				return;
			}

			// does not meet max dpi
			if (spec.dpi && cfg.maxDpi && spec.dpi > cfg.maxDpi) {
				return;
			}

			// has no localized path
			if (cfg.locale && !spec.localePath) {
				return;
			}

			// has no 9-patch path
			if (cfg.nine && spec.type === 'splash' && _.indexOf(spec.platforms, 'android') !== -1 && !(spec.ninePath || spec.alsoNine)) {
				return;
			}

			// does not meet at least one platform
			if (!_.some(spec.platforms, function (platform) {
					return !!cfg[platform];
				})) {

				return false;
			}

			// push initted spec
			_specs[name] = _initSpec(name, cfg);
		});

		// no specs not found
		if (_.size(_specs) === 0) {
			return callback('No specs found');
		}

		// pass back specs
		callback(null, _specs);
	});
};

function _initSpec(name, cfg) {

	// doesn't exist
	if (!specs[name]) {
		return;
	}

	// cloning
	var spec = _.clone(specs[name]);

	// name
	spec.name = name;

	// default dpi
	spec.dpi = spec.dpi || 72;

	// size to widthxheight
	if (spec.size) {
		spec.width = spec.height = spec.size;
	}

	// fix
	if (cfg.fix) {
		if (spec.widthFix) {
			spec.width = spec.widthFix;
		}
		if (spec.heightFix) {
			spec.height = spec.heightFix;
		}
	}

	// full output path
	var _path;

	if (cfg.nine && spec.ninePath) {
		_path = cfg.locale ? spec.nineLocalePath.replace(':locale:', cfg.locale) : spec.ninePath;

	} else {
		_path = (cfg.locale && spec.localePath) ? spec.localePath.replace(':locale:', cfg.locale) : spec.path;
		_path = _path.replace(':assets:', cfg.assetsDir);
	}

	spec.output = path.join(cfg.outputDir, _path);

	return spec;
}
<|endoftext|>


<commit_before>var $ = require('jquery');
var Backbone = require('backbone');
var mapbox = require('mapbox.js');
var enquire = require('enquire.js');

var MapView = Backbone.View.extend({
  initialize: function() {
    var token = 'mozilla-webprod.e91ef8b3';
    var map = this.map = L.mapbox.map(this.el.id, { trackResize: false });

    enquire.register("screen and (min-width: 768px)", this.setDefaultState.bind(this));
    enquire.register("screen and (max-width: 767px)", this.setMobileState.bind(this));

    this.isolationMode = false;

    var mapLayer = L.mapbox.tileLayer(token,{
        detectRetina: true
    });

    // when ready, set the map and page default states
    mapLayer.on('ready', function () {
      // touch support detection.
      var touch = L.Browser.touch || L.Browser.msTouch;
      // add tile layer to the map
      mapLayer.addTo(map);
      // disable map zoom on scroll.
      map.scrollWheelZoom.disable();
      // create spaces markers.
      this.initSpacesMarkers();

      // disable dragging for touch devices.
      if (touch) {
        // disable drag and zoom handlers.
        map.dragging.disable();
        map.touchZoom.disable();
        map.doubleClickZoom.disable();
        // disable tap handler, if present.
        if (map.tap) {
          map.tap.disable();
        }
      }
    }.bind(this));
  },

  setDefaultState: function() {
    // Bounds order is [South West, North East]
    var bounds = [[-45, -90], [45, 130]];
    this.map.fitBounds(bounds, { animate: false });
    this.map.setZoom(2);

    var windowHeight = $(window).height();
    var boundingBoxHeight = 766;
    this.map.panBy([0, (windowHeight - boundingBoxHeight) / 2], { reset: true });
  },

  setMobileState: function() {
    var bounds = [[-45, -90], [45, 130]];
    this.map.fitBounds(bounds, { animate: false });
    this.map.setZoom(0);

    this.map.panBy([0, -40], { animate: false });
  },

  /*
   * Creates spaces markers and then hide them using setFilter()
   */
  initSpacesMarkers: function () {
    var map = this.map;

    map.featureLayer.on('layeradd', function(e) {
      var marker = e.layer,
          feature = marker.feature;

      marker.setIcon(L.icon(feature.properties.icon));
      marker.selected = false;

      var unslug = feature.properties.id.replace(/-/g , " ");
      var popupContent = toTitleCase(unslug);
      marker.bindPopup(popupContent, {
        closeButton: false,
        closeOnClick: true
      });
    });

    $.getJSON('/static/mozspaces.json', function(mozSpaces) {
      map.featureLayer.setGeoJSON(mozSpaces);
      this.trigger('ready');
    }.bind(this));

    map.featureLayer.on('click', this.onMarkerClick.bind(this));
    map.featureLayer.on('mouseover', this.onMarkerMouseOver.bind(this));
    map.featureLayer.on('mouseout', this.onMarkerMouseOut.bind(this));
  },

  onMarkerMouseOver: function(e) {
    if (this.isolationMode && !e.layer.selected) {
      e.layer.setOpacity(0.75);
    }
    e.layer.openPopup();
  },

  onMarkerMouseOut: function(e) {
    if (this.isolationMode && !e.layer.selected) {
      e.layer.setOpacity(0.25);
    }
    e.layer.closePopup();
  },

  onMarkerClick: function(e) {
    this.map.featureLayer.eachLayer(function (marker) {
      if (marker !== e.layer) {
        marker.setOpacity(0.25);
        marker.selected = false;
      } else {
        marker.setOpacity(1);
        marker.selected = true;
      }
    });

    var markerId = e.layer.feature.properties.id;
    this.trigger('markerClick', markerId);
  },

  doClickMarker: function(marker) {
    marker.fireEvent('click');
  },

  selectMarker: function(id) {
    this.map.featureLayer.eachLayer(function(marker) {
      if (marker.feature.properties.id === id) {
        this.doClickMarker(marker);
        marker.closePopup();
      }
    }.bind(this));

    this.isolationMode = true;
  },

  deselectMarker: function() {
    this.map.featureLayer.eachLayer(function(marker) {
      marker.setOpacity(1);
      marker.selected = false;
    });

    this.isolationMode = false;
  },

  highlightMarker: function(id) {
    if (!this.isolationMode) {
      this.map.featureLayer.eachLayer(function(marker) {
        if (marker.feature.properties.id !== id) {
          marker.setOpacity(0.25)
        }
      });
    }
  },

  unHighlightAllMarkers: function(id) {
    if (!this.isolationMode) {
      this.map.featureLayer.eachLayer(function(marker) {
        marker.setOpacity(1);
      });
    }
  },

  hideSpacesMarkers: function() {
    this.map.featureLayer.setFilter(function () {
      return false;
    });
  },

  getMarkerOffset: function() {
    var offset = {};
    var tabletBreakPoint = 1010;
    var tabletMode = $(window).width() > tabletBreakPoint ? true : false;
    var pageWidth =  tabletMode ? 980 : 740;
    var markerHeight = 45;
    var rightMargin = tabletMode ? 80 : 60;
    var topMargin = 20;
    var headerOffset = $('.js-event-header').offset().top;
    offset.x = - (pageWidth / 2) + rightMargin;
    offset.y = (($(window).height() - ((headerOffset + markerHeight) * 2)) / 2) - topMargin;

    return offset;
  },

  focusSpace: function(id) {
    this.map.featureLayer.eachLayer(function (marker) {
      if (marker.feature.properties.id === id) {
        var markerCoords = this.markerCoords = marker.getLatLng();
        this.map.setView(markerCoords, 10, {
          animate: true
        });
      }
    }.bind(this));

    enquire.register("screen and (min-width: 768px)", function() {
      var offset = this.getMarkerOffset();
      this.map.panBy([offset.x, offset.y]);
    });
  }
});

function toTitleCase(str) {
  return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}

module.exports = MapView;<commit_msg>Fix missing context in enquire callback on event page<commit_after>var $ = require('jquery');
var Backbone = require('backbone');
var mapbox = require('mapbox.js');
var enquire = require('enquire.js');

var MapView = Backbone.View.extend({
  initialize: function() {
    var token = 'mozilla-webprod.e91ef8b3';
    var map = this.map = L.mapbox.map(this.el.id, { trackResize: false });

    enquire.register("screen and (min-width: 768px)", this.setDefaultState.bind(this));
    enquire.register("screen and (max-width: 767px)", this.setMobileState.bind(this));

    this.isolationMode = false;

    var mapLayer = L.mapbox.tileLayer(token,{
        detectRetina: true
    });

    // when ready, set the map and page default states
    mapLayer.on('ready', function () {
      // touch support detection.
      var touch = L.Browser.touch || L.Browser.msTouch;
      // add tile layer to the map
      mapLayer.addTo(map);
      // disable map zoom on scroll.
      map.scrollWheelZoom.disable();
      // create spaces markers.
      this.initSpacesMarkers();

      // disable dragging for touch devices.
      if (touch) {
        // disable drag and zoom handlers.
        map.dragging.disable();
        map.touchZoom.disable();
        map.doubleClickZoom.disable();
        // disable tap handler, if present.
        if (map.tap) {
          map.tap.disable();
        }
      }
    }.bind(this));
  },

  setDefaultState: function() {
    // Bounds order is [South West, North East]
    var bounds = [[-45, -90], [45, 130]];
    this.map.fitBounds(bounds, { animate: false });
    this.map.setZoom(2);

    var windowHeight = $(window).height();
    var boundingBoxHeight = 766;
    this.map.panBy([0, (windowHeight - boundingBoxHeight) / 2], { reset: true });
  },

  setMobileState: function() {
    var bounds = [[-45, -90], [45, 130]];
    this.map.fitBounds(bounds, { animate: false });
    this.map.setZoom(0);

    this.map.panBy([0, -40], { animate: false });
  },

  /*
   * Creates spaces markers and then hide them using setFilter()
   */
  initSpacesMarkers: function () {
    var map = this.map;

    map.featureLayer.on('layeradd', function(e) {
      var marker = e.layer,
          feature = marker.feature;

      marker.setIcon(L.icon(feature.properties.icon));
      marker.selected = false;

      var unslug = feature.properties.id.replace(/-/g , " ");
      var popupContent = toTitleCase(unslug);
      marker.bindPopup(popupContent, {
        closeButton: false,
        closeOnClick: true
      });
    });

    $.getJSON('/static/mozspaces.json', function(mozSpaces) {
      map.featureLayer.setGeoJSON(mozSpaces);
      this.trigger('ready');
    }.bind(this));

    map.featureLayer.on('click', this.onMarkerClick.bind(this));
    map.featureLayer.on('mouseover', this.onMarkerMouseOver.bind(this));
    map.featureLayer.on('mouseout', this.onMarkerMouseOut.bind(this));
  },

  onMarkerMouseOver: function(e) {
    if (this.isolationMode && !e.layer.selected) {
      e.layer.setOpacity(0.75);
    }
    e.layer.openPopup();
  },

  onMarkerMouseOut: function(e) {
    if (this.isolationMode && !e.layer.selected) {
      e.layer.setOpacity(0.25);
    }
    e.layer.closePopup();
  },

  onMarkerClick: function(e) {
    this.map.featureLayer.eachLayer(function (marker) {
      if (marker !== e.layer) {
        marker.setOpacity(0.25);
        marker.selected = false;
      } else {
        marker.setOpacity(1);
        marker.selected = true;
      }
    });

    var markerId = e.layer.feature.properties.id;
    this.trigger('markerClick', markerId);
  },

  doClickMarker: function(marker) {
    marker.fireEvent('click');
  },

  selectMarker: function(id) {
    this.map.featureLayer.eachLayer(function(marker) {
      if (marker.feature.properties.id === id) {
        this.doClickMarker(marker);
        marker.closePopup();
      }
    }.bind(this));

    this.isolationMode = true;
  },

  deselectMarker: function() {
    this.map.featureLayer.eachLayer(function(marker) {
      marker.setOpacity(1);
      marker.selected = false;
    });

    this.isolationMode = false;
  },

  highlightMarker: function(id) {
    if (!this.isolationMode) {
      this.map.featureLayer.eachLayer(function(marker) {
        if (marker.feature.properties.id !== id) {
          marker.setOpacity(0.25)
        }
      });
    }
  },

  unHighlightAllMarkers: function(id) {
    if (!this.isolationMode) {
      this.map.featureLayer.eachLayer(function(marker) {
        marker.setOpacity(1);
      });
    }
  },

  hideSpacesMarkers: function() {
    this.map.featureLayer.setFilter(function () {
      return false;
    });
  },

  getMarkerOffset: function() {
    var offset = {};
    var tabletBreakPoint = 1010;
    var tabletMode = $(window).width() > tabletBreakPoint ? true : false;
    var pageWidth =  tabletMode ? 980 : 740;
    var markerHeight = 45;
    var rightMargin = tabletMode ? 80 : 60;
    var topMargin = 20;
    var headerOffset = $('.js-event-header').offset().top;
    offset.x = - (pageWidth / 2) + rightMargin;
    offset.y = (($(window).height() - ((headerOffset + markerHeight) * 2)) / 2) - topMargin;

    return offset;
  },

  focusSpace: function(id) {
    this.map.featureLayer.eachLayer(function (marker) {
      if (marker.feature.properties.id === id) {
        var markerCoords = this.markerCoords = marker.getLatLng();
        this.map.setView(markerCoords, 10, {
          animate: true
        });
      }
    }.bind(this));

    enquire.register("screen and (min-width: 768px)", function() {
      var offset = this.getMarkerOffset();
      this.map.panBy([offset.x, offset.y]);
    }.bind(this));
  }
});

function toTitleCase(str) {
  return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}

module.exports = MapView;<|endoftext|>


<commit_before>var $ = require('jquery');
var Emitter = require('event-emitter');

require('./styles.scss');
var template = require('./view.twig');

var CLASSES = {
  OPENED: '_opened',
  ITEM_SELECTED: '_selected'
};

var EVENTS = {
  SELECT: 'select'
};

/**
 * @param {HTMLElement|string} node
 * @param {Object} config
 * @param {number} [config.selectedIndex=0]
 * @param {Function} [config.onSelect]
 * @constructor
 */
function Dropdown(node, config) {
  if (!(this instanceof Dropdown))
    return new Dropdown(node, config);

  this._emitter = Emitter({});
  var that = this;
  this.config = config;
  var $dropdown = this.render();
  this.$dropdown = $dropdown;
  var $items = $dropdown.find('.js-item');

  $dropdown.find('.js-selected-value').on('click', function (e) {
    e.stopPropagation();
    that.isOpened() ? that.close() : that.open();
  });

  $(document.body).on('click', this.close.bind(this));

  $items.each(function (i, elem) {
    $(elem).on('click', that.select.bind(that, i));
  });

  $(node).append($dropdown);

  config.onSelect && this.onSelect(config.onSelect);

  this.select(config.selectedIndex || 0, false);
}

Dropdown.prototype.onSelect = function (callback) {
  this._emitter.on(EVENTS.SELECT, callback);
};

Dropdown.prototype.render = function () {
  var config = this.config;
  var data = $.extend({}, {
    items: config.items,
    selectedIndex: config.selectedIndex || 0
  });

  var rendered = template.render({dropdown: data});
  return $(rendered);
};

Dropdown.prototype.open = function () {
  this.$dropdown.addClass(CLASSES.OPENED);
};

Dropdown.prototype.close = function () {
  this.$dropdown.removeClass(CLASSES.OPENED);
};

Dropdown.prototype.isOpened = function () {
  return this.$dropdown.hasClass(CLASSES.OPENED);
};

Dropdown.prototype.select = function (index, emit) {
  if (this.selectedIndex == index) {
    return;
  }
  this.selectedIndex = index;

  var emit = typeof emit == 'boolean' ? emit : true;
  var $dropdown = this.$dropdown;
  var $items = $dropdown.find('.js-item');
  var $selectedItem =  $( $items.get(index) );
  var selectedValue = $selectedItem.attr('data-value');
  var selectedText = $selectedItem.text();

  $items.each(function (i, elem) {
    var $item = $(elem);

    if (i === index)
      $item.addClass(CLASSES.ITEM_SELECTED);
    else
      $item.removeClass(CLASSES.ITEM_SELECTED);
  });

  $dropdown.find('.js-selected-value').text(selectedText);

  emit && this._emitter.emit('select', selectedValue);
};

module.exports = Dropdown;<commit_msg>Properly close dropdown list<commit_after>var $ = require('jquery');
var Emitter = require('event-emitter');

require('./styles.scss');
var template = require('./view.twig');

var CLASSES = {
  OPENED: '_opened',
  ITEM_SELECTED: '_selected'
};

var EVENTS = {
  SELECT: 'select'
};

/**
 * @param {HTMLElement|string} node
 * @param {Object} config
 * @param {number} [config.selectedIndex=0]
 * @param {Function} [config.onSelect]
 * @constructor
 */
function Dropdown(node, config) {
  if (!(this instanceof Dropdown))
    return new Dropdown(node, config);

  this._emitter = Emitter({});
  var that = this;
  this.config = config;
  var $dropdown = this.render();
  this.$dropdown = $dropdown;
  var $items = $dropdown.find('.js-item');
  var selectedValueNode = $dropdown.find('.js-selected-value').get(0);

  $(document.body).on('click', function(event) {
    event.target === selectedValueNode
      ? that.toggle()
      : that.close();
  });

  $items.each(function (i, elem) {
    $(elem).on('click', that.select.bind(that, i));
  });

  $(node).append($dropdown);

  config.onSelect && this.onSelect(config.onSelect);

  this.select(config.selectedIndex || 0, false);
}

Dropdown.prototype.onSelect = function (callback) {
  this._emitter.on(EVENTS.SELECT, callback);
};

Dropdown.prototype.render = function () {
  var config = this.config;
  var data = $.extend({}, {
    items: config.items,
    selectedIndex: config.selectedIndex || 0
  });

  var rendered = template.render({dropdown: data});
  return $(rendered);
};

Dropdown.prototype.open = function () {
  this.$dropdown.addClass(CLASSES.OPENED);
};

Dropdown.prototype.close = function () {
  this.$dropdown.removeClass(CLASSES.OPENED);
};

Dropdown.prototype.toggle = function() {
  this.isOpened() ? this.close() : this.open();
};

Dropdown.prototype.isOpened = function () {
  return this.$dropdown.hasClass(CLASSES.OPENED);
};

Dropdown.prototype.select = function (index, emit) {
  if (this.selectedIndex == index) {
    return;
  }
  this.selectedIndex = index;

  var emit = typeof emit == 'boolean' ? emit : true;
  var $dropdown = this.$dropdown;
  var $items = $dropdown.find('.js-item');
  var $selectedItem =  $( $items.get(index) );
  var selectedValue = $selectedItem.attr('data-value');
  var selectedText = $selectedItem.text();

  $items.each(function (i, elem) {
    var $item = $(elem);

    if (i === index)
      $item.addClass(CLASSES.ITEM_SELECTED);
    else
      $item.removeClass(CLASSES.ITEM_SELECTED);
  });

  $dropdown.find('.js-selected-value').text(selectedText);

  emit && this._emitter.emit('select', selectedValue);
};

module.exports = Dropdown;<|endoftext|>


<commit_before>/**
 * Creates keys for `private` properties on exposed objects to minimize interactions with other codebases.
 * The key will be a Symbol if the host supports it; otherwise a prefixed string.
 */
var create;

if (typeof Symbol === 'function') {
  create = function (name) {
    return Symbol(name);
  }
} else {
  create = function (name) {
    return '_zone$' + name;
  }
}

var commonKeys = {
  addEventListener: create('addEventListener'),
  removeEventListener: create('removeEventListener')
};

module.exports = {
  create: create,
  common: commonKeys
};
<commit_msg>fix(keys): Do not use Symbol which are broken in Chrome 39.0.2171 (Dartium)<commit_after>/**
 * Creates keys for `private` properties on exposed objects to minimize interactions with other codebases.
 */

function create(name) {
  // `Symbol` implementation is broken in Chrome 39.0.2171, do not use them even if they are available
  return '_zone$' + name;
}

var commonKeys = {
  addEventListener: create('addEventListener'),
  removeEventListener: create('removeEventListener')
};

module.exports = {
  create: create,
  common: commonKeys
};
<|endoftext|>


<commit_before>/**
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.
 *
 * Camunda licenses this file to you under the MIT; you may not use this file
 * except in compliance with the MIT License.
 */

var coverage = process.env.COVERAGE;

if (coverage) {
  // must set NODE_ENV to coverage to activate
  // babel-plugin-istanbul (cf. babel config)
  process.env.NODE_ENV = 'coverage';
}

var path = require('path');
var os = require('os');

var platform = os.platform();
var windows = /^win/.test(platform);

var { DefinePlugin } = require('webpack');

var absoluteBasePath = path.resolve(__dirname);

/* global process */

// configures browsers to run test against
// any of [ 'ChromeHeadless', 'Chrome', 'Firefox', 'IE', 'PhantomJS' ]
var browsers =
  (process.env.TEST_BROWSERS || 'ChromeHeadless')
    .replace(/^\s+|\s+$/, '')
    .split(/\s*,\s*/g)
    .map(function(browser) {
      if (browser === 'ChromeHeadless') {
        process.env.CHROME_BIN = require('puppeteer').executablePath();

        // workaround https://github.com/GoogleChrome/puppeteer/issues/290
        if (process.platform === 'linux') {
          return 'ChromeHeadless_Linux';
        }
      }

      return browser;
    });

var suite = coverage ? 'test/all.js' : 'test/suite.js';

module.exports = function(karma) {
  karma.set({

    frameworks: [
      'mocha',
      'sinon-chai'
    ],

    files: [
      suite
    ],

    preprocessors: {
      [suite]: [ 'webpack', 'env' ]
    },

    reporters: [ 'spec' ].concat(coverage ? 'coverage' : []),

    customLaunchers: {
      ChromeHeadless_Linux: {
        base: 'ChromeHeadless',
        flags: [
          '--no-sandbox',
          '--disable-setuid-sandbox'
        ],
        debug: true
      }
    },

    coverageReporter: {
      reporters: [
        { type: 'lcov', subdir: '.' }
      ]
    },

    browsers: browsers,

    browserNoActivityTimeout: 30000,

    singleRun: true,
    autoWatch: false,

    webpack: {
      mode: 'development',
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: 'babel-loader'
          },
          {
            test: /\.(css|bpmn|cmmn|dmn|less|xml|png|svg)$/,
            use: 'raw-loader'
          }
        ]
      },
      plugins: [
        new DefinePlugin({
          'process.env': {
            NODE_ENV: JSON.stringify('test'),
            WINDOWS: JSON.stringify(windows)
          }
        })
      ],
      resolve: {
        mainFields: [
          'dev:module',
          'browser',
          'module',
          'main'
        ],
        modules: [
          'node_modules',
          absoluteBasePath
        ],
        alias: {
          'bpmn-js/lib/Modeler': 'test/mocks/bpmn-js/Modeler',
          'cmmn-js/lib/Modeler': 'test/mocks/cmmn-js/Modeler',
          'dmn-js/lib/Modeler': 'test/mocks/dmn-js/Modeler',
          './DmnModeler': 'test/mocks/dmn-js/Modeler',
          './CodeMirror': 'test/mocks/code-mirror/CodeMirror',
          'sourcemapped-stacktrace': 'test/mocks/sourcemapped-stacktrace'
        }
      }
    }
  });
};
<commit_msg>chore(client): convert karma config to unix-style endline<commit_after>/**
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.
 *
 * Camunda licenses this file to you under the MIT; you may not use this file
 * except in compliance with the MIT License.
 */

var coverage = process.env.COVERAGE;

if (coverage) {
  // must set NODE_ENV to coverage to activate
  // babel-plugin-istanbul (cf. babel config)
  process.env.NODE_ENV = 'coverage';
}

var path = require('path');
var os = require('os');

var platform = os.platform();
var windows = /^win/.test(platform);

var { DefinePlugin } = require('webpack');

var absoluteBasePath = path.resolve(__dirname);

/* global process */

// configures browsers to run test against
// any of [ 'ChromeHeadless', 'Chrome', 'Firefox', 'IE', 'PhantomJS' ]
var browsers =
  (process.env.TEST_BROWSERS || 'ChromeHeadless')
    .replace(/^\s+|\s+$/, '')
    .split(/\s*,\s*/g)
    .map(function(browser) {
      if (browser === 'ChromeHeadless') {
        process.env.CHROME_BIN = require('puppeteer').executablePath();

        // workaround https://github.com/GoogleChrome/puppeteer/issues/290
        if (process.platform === 'linux') {
          return 'ChromeHeadless_Linux';
        }
      }

      return browser;
    });

var suite = coverage ? 'test/all.js' : 'test/suite.js';

module.exports = function(karma) {
  karma.set({

    frameworks: [
      'mocha',
      'sinon-chai'
    ],

    files: [
      suite
    ],

    preprocessors: {
      [suite]: [ 'webpack', 'env' ]
    },

    reporters: [ 'spec' ].concat(coverage ? 'coverage' : []),

    customLaunchers: {
      ChromeHeadless_Linux: {
        base: 'ChromeHeadless',
        flags: [
          '--no-sandbox',
          '--disable-setuid-sandbox'
        ],
        debug: true
      }
    },

    coverageReporter: {
      reporters: [
        { type: 'lcov', subdir: '.' }
      ]
    },

    browsers: browsers,

    browserNoActivityTimeout: 30000,

    singleRun: true,
    autoWatch: false,

    webpack: {
      mode: 'development',
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: 'babel-loader'
          },
          {
            test: /\.(css|bpmn|cmmn|dmn|less|xml|png|svg)$/,
            use: 'raw-loader'
          }
        ]
      },
      plugins: [
        new DefinePlugin({
          'process.env': {
            NODE_ENV: JSON.stringify('test'),
            WINDOWS: JSON.stringify(windows)
          }
        })
      ],
      resolve: {
        mainFields: [
          'dev:module',
          'browser',
          'module',
          'main'
        ],
        modules: [
          'node_modules',
          absoluteBasePath
        ],
        alias: {
          'bpmn-js/lib/Modeler': 'test/mocks/bpmn-js/Modeler',
          'cmmn-js/lib/Modeler': 'test/mocks/cmmn-js/Modeler',
          'dmn-js/lib/Modeler': 'test/mocks/dmn-js/Modeler',
          './DmnModeler': 'test/mocks/dmn-js/Modeler',
          './CodeMirror': 'test/mocks/code-mirror/CodeMirror',
          'sourcemapped-stacktrace': 'test/mocks/sourcemapped-stacktrace'
        }
      }
    }
  });
};
<|endoftext|>


<commit_before>// XXX There is no reason to define these shortcuts globally, they should be
// attached to a template (most of them will go in the `board` template).

Mousetrap.bind('?', () => {
  FlowRouter.go('shortcuts');
});

Mousetrap.bind('w', () => {
  if (Sidebar.isOpen() && Sidebar.getView() === 'home') {
    Sidebar.toggle();
  } else {
    Sidebar.setView();
  }
});

Mousetrap.bind('q', () => {
  const currentBoardId = Session.get('currentBoard');
  const currentUserId = Meteor.userId();
  if (currentBoardId && currentUserId) {
    Filter.members.toggle(currentUserId);
  }
});

Mousetrap.bind('x', () => {
  if (Filter.isActive()) {
    Filter.reset();
  }
});

Mousetrap.bind('f', () => {
  if (Sidebar.isOpen() && Sidebar.getView() === 'filter') {
    Sidebar.toggle();
  } else {
    Sidebar.setView('filter');
  }
});

Mousetrap.bind(['down', 'up'], (evt, key) => {
  if (!Session.get('currentCard')) {
    return;
  }

  const nextFunc = key === 'down' ? 'next' : 'prev';
  const nextCard = $('.js-minicard.is-selected')
    [nextFunc]('.js-minicard')
    .get(0);
  if (nextCard) {
    const nextCardId = Blaze.getData(nextCard)._id;
    Utils.goCardId(nextCardId);
  }
});

// XXX This shortcut should also work when hovering over a card in board view
Mousetrap.bind('space', evt => {
  if (!Session.get('currentCard')) {
    return;
  }

  const currentUserId = Meteor.userId();
  if (currentUserId === null) {
    return;
  }

  if (Meteor.user().isBoardMember()) {
    const card = Cards.findOne(Session.get('currentCard'));
    card.toggleMember(currentUserId);
    // We should prevent scrolling in card when spacebar is clicked
    // This should do it according to Mousetrap docs, but it doesn't
    evt.preventDefault();
  }
});

Template.keyboardShortcuts.helpers({
  mapping: [
    {
      keys: ['W'],
      action: 'shortcut-toggle-sidebar',
    },
    {
      keys: ['Q'],
      action: 'shortcut-filter-my-cards',
    },
    {
      keys: ['F'],
      action: 'shortcut-toggle-filterbar',
    },
    {
      keys: ['X'],
      action: 'shortcut-clear-filters',
    },
    {
      keys: ['?'],
      action: 'shortcut-show-shortcuts',
    },
    {
      keys: ['ESC'],
      action: 'shortcut-close-dialog',
    },
    {
      keys: ['@'],
      action: 'shortcut-autocomplete-members',
    },
    {
      keys: ['SPACE'],
      action: 'shortcut-assign-self',
    },
  ],
});
<commit_msg>More keyboard shortcuts: c for archive card<commit_after>// XXX There is no reason to define these shortcuts globally, they should be
// attached to a template (most of them will go in the `board` template).

Mousetrap.bind('?', () => {
  FlowRouter.go('shortcuts');
});

Mousetrap.bind('w', () => {
  if (Sidebar.isOpen() && Sidebar.getView() === 'home') {
    Sidebar.toggle();
  } else {
    Sidebar.setView();
  }
});

Mousetrap.bind('q', () => {
  const currentBoardId = Session.get('currentBoard');
  const currentUserId = Meteor.userId();
  if (currentBoardId && currentUserId) {
    Filter.members.toggle(currentUserId);
  }
});

Mousetrap.bind('x', () => {
  if (Filter.isActive()) {
    Filter.reset();
  }
});

Mousetrap.bind('f', () => {
  if (Sidebar.isOpen() && Sidebar.getView() === 'filter') {
    Sidebar.toggle();
  } else {
    Sidebar.setView('filter');
  }
});

Mousetrap.bind(['down', 'up'], (evt, key) => {
  if (!Session.get('currentCard')) {
    return;
  }

  const nextFunc = key === 'down' ? 'next' : 'prev';
  const nextCard = $('.js-minicard.is-selected')
    [nextFunc]('.js-minicard')
    .get(0);
  if (nextCard) {
    const nextCardId = Blaze.getData(nextCard)._id;
    Utils.goCardId(nextCardId);
  }
});

// XXX This shortcut should also work when hovering over a card in board view
Mousetrap.bind('space', evt => {
  if (!Session.get('currentCard')) {
    return;
  }

  const currentUserId = Meteor.userId();
  if (currentUserId === null) {
    return;
  }

  if (Meteor.user().isBoardMember()) {
    const card = Cards.findOne(Session.get('currentCard'));
    card.toggleMember(currentUserId);
    // We should prevent scrolling in card when spacebar is clicked
    // This should do it according to Mousetrap docs, but it doesn't
    evt.preventDefault();
  }
});

// XXX This shortcut should also work when hovering over a card in board view
Mousetrap.bind('c', evt => {
  if (!Session.get('currentCard')) {
    return;
  }

  const currentUserId = Meteor.userId();
  if (currentUserId === null) {
    return;
  }

  if (
    Meteor.user().isBoardMember() &&
    !Meteor.user().isCommentOnly() &&
    !Meteor.user().isWorker()
  ) {
    const card = Cards.findOne(Session.get('currentCard'));
    card.archive();
    // We should prevent scrolling in card when spacebar is clicked
    // This should do it according to Mousetrap docs, but it doesn't
    evt.preventDefault();
  }
});

Template.keyboardShortcuts.helpers({
  mapping: [
    {
      keys: ['W'],
      action: 'shortcut-toggle-sidebar',
    },
    {
      keys: ['Q'],
      action: 'shortcut-filter-my-cards',
    },
    {
      keys: ['F'],
      action: 'shortcut-toggle-filterbar',
    },
    {
      keys: ['X'],
      action: 'shortcut-clear-filters',
    },
    {
      keys: ['?'],
      action: 'shortcut-show-shortcuts',
    },
    {
      keys: ['ESC'],
      action: 'shortcut-close-dialog',
    },
    {
      keys: ['@'],
      action: 'shortcut-autocomplete-members',
    },
    {
      keys: ['SPACE'],
      action: 'shortcut-assign-self',
    },
    {
      keys: ['C'],
      action: 'archive-card',
    },
  ],
});
<|endoftext|>


<commit_before>/**
 * Autoinsert script tags (or other filebased tags) in an html file.
 *
 * ---------------------------------------------------------------
 *
 * Automatically inject <script> tags for javascript files and <link> tags
 * for css files.  Also automatically links an output file containing precompiled
 * templates using a <script> tag.
 *
 * For usage docs see:
 * 		https://github.com/Zolmeister/grunt-sails-linker
 *
 */
// todo - add css and templates sections to layout.ejs. add prod,dev, etc
module.exports = function(gulp, plugins) {
	gulp.task('linker',['scripts'], function() {
		// Read templates
		return gulp.src('views/layout.ejs')
				// Link the JavaScript
				.pipe(plugins.linker({
					scripts: ['.tmp/public/js/**.js'],
					startTag: '<!--SCRIPTS-->',
					endTag: '<!--SCRIPTS END-->',
					fileTmpl: '<script src="%s"></script>',
					appRoot: '.tmp/public',
					relative: true
				}))
				// Write modified files to www/
				.pipe(gulp.dest('views/'))
				.pipe(plugins.notify({ message: 'Linker task complete' }));
	  });
  };
<commit_msg>added styles and templates to linker<commit_after>/**
 * Autoinsert script tags (or other filebased tags) in an html file.
 *
 * ---------------------------------------------------------------
 *
 * Automatically inject <script> tags for javascript files and <link> tags
 * for css files.  Also automatically links an output file containing precompiled
 * templates using a <script> tag.
 *
 * For usage docs see:
 * 		https://github.com/Zolmeister/grunt-sails-linker
 *
 */
// todo - add css and templates sections to layout.ejs. add prod,dev, etc
module.exports = function(gulp, plugins) {
	gulp.task('linker',['scripts', 'styles'], function() {
		// Read templates
		return gulp.src(['.tmp/public/**/*.html', 'views/**/*.html', 'views/**/*.ejs'])
				// Link the JavaScript
				.pipe(plugins.linker({
					scripts: [require('../pipeline').jsFilesToInject],
					startTag: '<!--SCRIPTS-->',
					endTag: '<!--SCRIPTS END-->',
					fileTmpl: '<script src="%s"></script>',
					appRoot: '.tmp/public',
					relative: true
				}))
				// Link the Styles
				.pipe(plugins.linker({
					scripts: [require('../pipeline').cssFilesToInject],
					startTag: '<!--STYLES-->',
					endTag: '<!--STYLES END-->',
					fileTmpl: '<link rel="stylesheet" href="%s">',
					appRoot: '.tmp/public',
					relative: true
				}))
				// Link the JST Templates
				.pipe(plugins.linker({
					scripts: ['.tmp/public/jst.js'],
					startTag: '<!--TEMPLATES-->',
					endTag: '<!--TEMPLATES END-->',
					fileTmpl: '<script type="text/javascript" src="%s"></script>',
					appRoot: '.tmp/public',
					relative: true
				}))
				// Write modified files to www/
				.pipe(gulp.dest('views/'))
				.pipe(plugins.notify({ message: 'Linker task complete' }));
	  });
  };
<|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var SerialPort = require('serialport').SerialPort;

var app = express();

app.use(bodyParser.json());

app.get('/trafficlight', function(req, res) {
    res.sendStatus(501);
});

app.put('/trafficlight', function(req, res) {
    // body can look like: { "light": "on|flashing|off", "lamp": "red|yellow|green" }

    var light = req.body.light;
    var lamp = req.body.lamp;
    var lamps = ['red', 'yellow', 'green'];

    if (light === 'on' || light === 'flashing') {
        if (!lamp || lamps.indexOf(lamp) === -1) {
            res.status(400).send('Invalid value specified for `lamp`.');
        }
        else {
            var command = light === 'on' ? 'light' : 'flash';

            serialPort.write(command + ' ' + lamp);

            console.log(command + ' ' + lamp);

            res.sendStatus(204);
        }
    }
    else if (light === 'off') {
        serialPort.write('lights off');

        res.sendStatus(204);
    }
    else {
        res.status(400).send('Invalid value specified for `light`.');
    }
});

var portName = '/dev/tty.usbmodem1421';
var serialPort = new SerialPort(portName, { baudRate: 9600 });

serialPort.open(function(error) {
    serialPort.on('data', function(data) {
      console.log('data received: ' + data);
    });

    console.log('Serial port opened, starting server.');

    var server = app.listen(8080);

    serialPort.write('light green');
});
<commit_msg>Deleted some old debugging lines.<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var SerialPort = require('serialport').SerialPort;

var app = express();

app.use(bodyParser.json());

app.get('/trafficlight', function(req, res) {
    res.sendStatus(501);
});

app.put('/trafficlight', function(req, res) {
    // body can look like: { "light": "on|flashing|off", "lamp": "red|yellow|green" }

    var light = req.body.light;
    var lamp = req.body.lamp;
    var lamps = ['red', 'yellow', 'green'];

    if (light === 'on' || light === 'flashing') {
        if (!lamp || lamps.indexOf(lamp) === -1) {
            res.status(400).send('Invalid value specified for `lamp`.');
        }
        else {
            var command = light === 'on' ? 'light' : 'flash';

            serialPort.write(command + ' ' + lamp);

            console.log(command + ' ' + lamp);

            res.sendStatus(204);
        }
    }
    else if (light === 'off') {
        serialPort.write('lights off');

        res.sendStatus(204);
    }
    else {
        res.status(400).send('Invalid value specified for `light`.');
    }
});

var portName = '/dev/tty.usbmodem1421';
var serialPort = new SerialPort(portName, { baudRate: 9600 });

serialPort.open(function(error) {
    console.log('Serial port opened, starting server.');

    var server = app.listen(8080);
});
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import RaisedButton from 'material-ui/RaisedButton'
import HeartBorderIcon from 'material-ui/svg-icons/action/favorite-border'
import Divider from 'material-ui/Divider'
import { dividerColor, dashboardIconActiveColor } from 'js/theme/default'
import DashboardPopover from 'js/components/Dashboard/DashboardPopover'
import { goToInviteFriends, goToDonate } from 'js/navigation/navigation'
// import Typography from '@material-ui/core/Typography'

const HeartsDropdownComponent = props => {
  const { anchorElement, app, onClose, open, user } = props

  const dividerStyle = {
    marginTop: 16,
    marginBottom: 12,
  }
  const statTextStyle = {
    fontSize: 14,
    display: 'block',
    marginTop: 4,
    marginBottom: 4,
  }
  const smallHeartIconStyle = {
    height: 16,
    marginLeft: -3,
  }
  const statNumberStyle = {
    fontSize: 24,
    display: 'block',
  }
  const popoverButtonStyle = {
    marginTop: 6,
    marginBottom: 0,
  }
  const popoverButtonLabelStyle = {
    fontSize: 13,
  }

  const rewardMethodContainerStyle = Object.assign({}, statTextStyle, {
    display: 'flex',
    textAlign: 'left',
    color: dividerColor,
    marginTop: 1,
    marginBottom: 1,
  })
  const rewardTextStyle = {
    textAlign: 'left',
    flex: 6,
  }
  const rewardValueStyle = {
    flex: 3,
    textAlign: 'right',
  }

  return (
    <DashboardPopover
      open={open}
      anchorEl={anchorElement}
      onClose={onClose}
      style={{
        textAlign: 'center',
        width: 210,
      }}
    >

            Level {user.level}

            {user.heartsUntilNextLevel}
            <HeartBorderIcon
              style={smallHeartIconStyle}
              color={dashboardIconActiveColor}
            />
             until next level

        <Divider style={dividerStyle} />

              {user.vcDonatedAllTime}
              <HeartBorderIcon
                style={{
                  marginLeft: 2,
                  height: 24,
                  width: 24,
                  paddingBottom: 1,
                }}
                color={dashboardIconActiveColor}
              />

            Donated

            <RaisedButton
              label="Donate Hearts"
              style={popoverButtonStyle}
              labelStyle={popoverButtonLabelStyle}
              primary
              onClick={goToDonate}
            />

        <Divider style={dividerStyle} />

            {user.numUsersRecruited}{' '}
            Tabbers Recruited

            <RaisedButton
              label="Invite A Friend"
              labelPosition="before"
              style={popoverButtonStyle}
              labelStyle={popoverButtonLabelStyle}
              primary
              onClick={goToInviteFriends}
            />

        <Divider style={dividerStyle} />

            Open a tab

              1
              <HeartBorderIcon
                style={Object.assign({}, smallHeartIconStyle, {
                  marginRight: -4,
                })}
                color={dividerColor}
              />

            Recruit a friend

              {app.referralVcReward}
              <HeartBorderIcon
                style={Object.assign({}, smallHeartIconStyle, {
                  marginRight: -4,
                })}
                color={dividerColor}
              />

    </DashboardPopover>
  )
}

HeartsDropdownComponent.displayNamae = 'HeartsDropdownComponent'

HeartsDropdownComponent.propTypes = {
  anchorElement: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
  app: PropTypes.shape({
    referralVcReward: PropTypes.number.isRequired,
  }),
  user: PropTypes.shape({
    vcCurrent: PropTypes.number.isRequired,
    level: PropTypes.number.isRequired,
    heartsUntilNextLevel: PropTypes.number.isRequired,
    vcDonatedAllTime: PropTypes.number.isRequired,
    numUsersRecruited: PropTypes.number.isRequired,
    tabsToday: PropTypes.number.isRequired,
  }),
  onClose: PropTypes.func.isRequired,
  open: PropTypes.bool.isRequired,
}

HeartsDropdownComponent.defaultProps = {
  open: false,
}

export default HeartsDropdownComponent
<commit_msg>Use Typography component in hearts dropdown<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
import Typography from '@material-ui/core/Typography'
import RaisedButton from 'material-ui/RaisedButton'
import HeartBorderIcon from 'material-ui/svg-icons/action/favorite-border'
import Divider from 'material-ui/Divider'
import { dividerColor, dashboardIconActiveColor } from 'js/theme/default'
import DashboardPopover from 'js/components/Dashboard/DashboardPopover'
import { goToInviteFriends, goToDonate } from 'js/navigation/navigation'

// TODO: break out to make the component customizable:
// https://material-ui.com/customization/overrides/#3-specific-variation-of-a-component
const styles = {
  root: {
    color: 'white',
  },
}

const HeartsDropdownComponent = props => {
  const { anchorElement, app, classes, onClose, open, user } = props

  const dividerStyle = {
    marginTop: 16,
    marginBottom: 12,
  }
  const popoverButtonStyle = {
    marginTop: 6,
    marginBottom: 0,
  }
  const popoverButtonLabelStyle = {
    fontSize: 13,
  }

  return (
    <DashboardPopover open={open} anchorEl={anchorElement} onClose={onClose}>

          <Typography
            classes={{
              root: classes.root,
            }}
            variant={'h5'}
            color={'primary'}
          >
            Level {user.level}
          </Typography>

            <Typography
              classes={{
                root: classes.root,
              }}
              variant={'body2'}
            >
              {user.heartsUntilNextLevel}
            </Typography>
            <HeartBorderIcon
              style={{
                height: 16, // TODO: use body2 font size
              }}
              color={dashboardIconActiveColor}
            />
            <Typography
              classes={{
                root: classes.root,
              }}
              variant={'body2'}
            >
              {' '}
              until next level
            </Typography>

        <Divider style={dividerStyle} />

              <Typography
                classes={{
                  root: classes.root,
                }}
                variant={'h5'}
              >
                {user.vcDonatedAllTime}
              </Typography>
              <HeartBorderIcon
                style={{
                  marginLeft: 2,
                  height: 24,
                  width: 24,
                  paddingBottom: 1,
                }}
                color={dashboardIconActiveColor}
              />

            <Typography
              classes={{
                root: classes.root,
              }}
              variant={'body2'}
            >
              donated
            </Typography>

            <RaisedButton
              label="Donate Hearts"
              style={popoverButtonStyle}
              labelStyle={popoverButtonLabelStyle}
              primary
              onClick={goToDonate}
            />

        <Divider style={dividerStyle} />

            <Typography
              classes={{
                root: classes.root,
              }}
              variant={'h5'}
            >
              {user.numUsersRecruited}
            </Typography>
            <Typography
              classes={{
                root: classes.root,
              }}
              variant={'body2'}
            >
              Tabbers Recruited
            </Typography>

            <RaisedButton
              label="Invite A Friend"
              labelPosition="before"
              style={popoverButtonStyle}
              labelStyle={popoverButtonLabelStyle}
              primary
              onClick={goToInviteFriends}
            />

        <Divider style={dividerStyle} />

            <Typography
              classes={{
                root: classes.root,
              }}
              variant={'body2'}
              style={{
                flex: 6,
              }}
            >
              Open a tab
            </Typography>

              <Typography
                classes={{
                  root: classes.root,
                }}
                variant={'body2'}
                style={{ textAlign: 'right' }}
              >
                1
              </Typography>
              <HeartBorderIcon
                style={{
                  height: 16,
                }}
                color={dividerColor}
              />

            <Typography
              classes={{
                root: classes.root,
              }}
              variant={'body2'}
              style={{
                flex: 6,
              }}
            >
              Recruit a friend
            </Typography>

              <Typography
                classes={{
                  root: classes.root,
                }}
                variant={'body2'}
                style={{ textAlign: 'right' }}
              >
                {app.referralVcReward}
              </Typography>
              <HeartBorderIcon
                style={{
                  height: 16,
                }}
                color={dividerColor}
              />

    </DashboardPopover>
  )
}

HeartsDropdownComponent.displayNamae = 'HeartsDropdownComponent'

HeartsDropdownComponent.propTypes = {
  anchorElement: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
  app: PropTypes.shape({
    referralVcReward: PropTypes.number.isRequired,
  }),
  classes: PropTypes.object.isRequired,
  user: PropTypes.shape({
    vcCurrent: PropTypes.number.isRequired,
    level: PropTypes.number.isRequired,
    heartsUntilNextLevel: PropTypes.number.isRequired,
    vcDonatedAllTime: PropTypes.number.isRequired,
    numUsersRecruited: PropTypes.number.isRequired,
    tabsToday: PropTypes.number.isRequired,
  }),
  onClose: PropTypes.func.isRequired,
  open: PropTypes.bool.isRequired,
}

HeartsDropdownComponent.defaultProps = {
  open: false,
}

export default withStyles(styles)(HeartsDropdownComponent)
<|endoftext|>