<commit_before>'use strict';

YUI.add('juju-models', function(Y) {

var models = Y.namespace('juju.models');

var Charm = Y.Base.create('charm', Y.Model, [], {
    idAttribute: 'charm_id',
    charm_id_re: /((\w+):)?(\w+)\/(\S+)-(\d+)/,
    parse_charm_id: function(id) {
        if (!id) { id = this.get('id'); }
        return this.charm_id_re.exec(id);
        }
    }, {
    ATTRS: {
        charm_id: {},
        name: {
            valueFn: function(name) {
                var match = this.parse_charm_id();
                if (match) {
                    return match[3] + '/' + match[4];
                }
            }
        },
        url: {},
        description: {},
        config: {},
        metadata: {},
        sha256: {}
    }
});
models.Charm = Charm;

var CharmList = Y.Base.create('charmList', Y.ModelList, [], {
    model: Charm
    }, {
    ATTRS: {
    }
});
models.CharmList = CharmList;

var Service = Y.Base.create('service', Y.Model, [], {
//    idAttribute: 'name',

    ATTRS: {
        name: {},
        charm: {},
        config: {},
        constraints: {},
        exposed: {value: false},
        relations: {},
        unit_count: {},
        aggregated_status: {}
    }
});
models.Service = Service;

var ServiceList = Y.Base.create('serviceList', Y.ModelList, [], {
    model: Service
    }, {
    ATTRS: {
    }
});
models.ServiceList = ServiceList;

var ServiceUnit = Y.Base.create('serviceUnit', Y.Model, [], {},
//    idAttribute: 'name',
    {
    ATTRS: {
        service: {
            valueFn: function(name) {
                var unit_name = this.get('id');
                return unit_name.split('/', 1)[0];
            }
        },
        number: {
            valueFn: function(name) {
                var unit_name = this.get('id');
                return parseInt(unit_name.split('/')[1], 10);
            }
        },
        machine: {},
        agent_state: {},
        // relations to unit relation state.
        relations: {},

        config: {},
        is_subordinate: {
            value: false // default
        },
        open_ports: {},
        public_address: {},
        private_address: {}
    }
});
models.ServiceUnit = ServiceUnit;

var ServiceUnitList = Y.Base.create('serviceUnitList', Y.LazyModelList, [], {
    model: ServiceUnit,
    get_units_for_service: function(service, asList) {
        var options = {},
            sid = service.get('id');

        if (asList !== undefined) {
            options.asList = true;
        }

        var units = this.filter(options, function(m) {
            return m.get('service') == sid;
        });
        return units;
    },
    /*
     *  Return information about the state of all units focused on the
     * 'worst' status information available. In this way if any unit is in
     *  an error state we can report that for the whole list.
     *
     * KT - Should return an aggregate count by state (map), so proproptional
     * representation can be done in various renderings.
     */
    get_informative_state: function() {
    },
    /*
     *  Return information about the state of the set of units for a
     *  given service in the form of a map of agent states:
     *  state => number of units in that state
     */
    get_informative_states_for_service: function(service) {
        var aggregate_map = {}, aggregate_list = [],
            units_for_service = this.get_units_for_service(service);

        units_for_service.forEach(function(unit) {
            var state = unit.get('agent_state');
            if (aggregate_map[state] === undefined) {
                aggregate_map[state] = 1;
            } else {
                aggregate_map[state]++;
            }

        });

        return aggregate_map;
    },

    /*
     * Updates a service's unit count and aggregate state map during a
     * delta, ensuring that they're up to date.
     */
    update_service_unit_aggregates: function(service) {
        var aggregate = this.get_informative_states_for_service(service);
        var sum = 0;
        for (var agent_state in aggregate) {
            sum += aggregate[agent_state];
        }
        service.set('unit_count', sum);
        service.set('aggregated_status', aggregate);
    },
    ATTRS: {
    }
});
models.ServiceUnitList = ServiceUnitList;

var Machine = Y.Base.create('machine', Y.Model, [], {
    idAttribute: 'machine_id'
    }, {
    ATTRS: {
        machine_id: {},
        public_address: {},
        instance_id: {},
    instance_state: {},
    agent_state: {}
    }
});
models.Machine = Machine;

var MachineList = Y.Base.create('machineList', Y.ModelList, [], {
    model: Machine
    }, {
    ATTRS: {
    }
});
models.MachineList = MachineList;

var Relation = Y.Base.create('relation', Y.Model, [], {
    idAttribute: 'relation_id'
    }, {
    ATTRS: {
        relation_id: {},
        type: {},
        endpoints: {}
    }
});
models.Relation = Relation;

var RelationList = Y.Base.create('relationList', Y.ModelList, [], {}, {
    ATTRS: {
    }
});
models.RelationList = RelationList;

var Database = Y.Base.create('database', Y.Base, [], {
    initializer: function() {
        this.services = new ServiceList();
        this.machines = new MachineList();
        this.charms = new CharmList();
        this.relations = new RelationList();

        // This one is dangerous.. we very well may not have capacity
        // to store a 1-1 representation of units in js.
        // At least we should never assume the collection is complete, and
        // have usage of some ephemeral slice/cursor of the collection.
        // Indexed db might be interesting to explore here, with object delta
        // and bulk transfer feeding directly into indexedb.
        // Needs some experimentation with a large data set.
        this.units = new ServiceUnitList();

        // For model syncing by type. Charms aren't currently sync'd, only fetched
        // on demand (they are static).
        this.model_map = {
            'unit': ServiceUnit,
            'machine': Machine,
            'service': Service,
            'relation': Relation,
            'charm': Charm
        };
    },

    reset: function() {
        this.services.reset();
        this.machines.reset();
        this.charms.reset();
        this.relations.reset();
        this.units.reset();
    },

    on_delta: function(delta_evt) {
        var changes = delta_evt.data.result;
        console.log('Delta', this, changes);
        var change_type, model_class = null, self = this;

        changes.forEach(
            Y.bind(function(change) {
                change_type = change[0];
                model_class = this.model_map[change_type];

                if (!model_class) {
                    console.log('Unknown Change', change);
                }
                console.log('change', this, change);
                var model_list = this[change_type + 's'];
                this.process_model_delta(change, model_class, model_list);
            }, this));
        this.services.each(function(service) {
            self.units.update_service_unit_aggregates(service);
        });
        this.fire('update');
    },

    // utility method to sync a data object and a model object.
    _sync_bag: function(bag, model) {
        // TODO: need to bulk mutate and then send mutation event,
        // as is this is doing per attribute events.
        for (var vid in bag) {
            var value = bag[vid];
            var aid = vid.replace('-', '_');
            if (model.get(aid) != value) {
                model.set(aid, value);
            }
        }
    },

    process_model_delta: function(change, ModelClass, model_list) {
        // console.log('model change', change);
        var change_kind = change[1];
        var data = change[2];
        var o;
        if (change_kind == 'add' || change_kind == 'change') {
            // Client-side requests may create temporary objects in the
            // database in order to give the user more immediate feedback.
            // The temporary objects are created after the ACK message from
            // the server that contains their actual names.  When the delta
            // arrives for those objects, they already exist in a skeleton
            // form that needs to be fleshed out.  So, the existing objects
            // are kept and re-used.
            o = model_list.getById(data.id);
            if (Y.Lang.isNull(o)) {
                o = new ModelClass({id: data.id});
                this._sync_bag(data, o);
                model_list.add(o);
            } else {
                this._sync_bag(data, o);
            }
        } else if (change_kind == 'remove') {
            o = model_list.getById(data);
            // This should only be necessary while we are waiting on server
            // side changes to have deltas aware of client requests.
            if (!Y.Lang.isNull(o)) {
                model_list.remove(o);
            }
        } else {
            console.log('Unknown change kind in process_model_delta: ',
                        change_kind);
        }
    }

});

models.Database = Database;

Y.namespace('juju').db = new Database({});

}, '0.1.0', {
    requires: [
        'model',
        'model-list',
        'lazy-model-list'
    ]
});
<commit_msg>[TRIVIAL] Undo unintended change from the previous commit.<commit_after>'use strict';

YUI.add('juju-models', function(Y) {

var models = Y.namespace('juju.models');

var Charm = Y.Base.create('charm', Y.Model, [], {
    idAttribute: 'charm_id',
    charm_id_re: /((\w+):)?(\w+)\/(\S+)-(\d+)/,
    parse_charm_id: function(id) {
        if (!id) { id = this.get('id'); }
        return this.charm_id_re.exec(id);
        }
    }, {
    ATTRS: {
        charm_id: {},
        name: {
            valueFn: function(name) {
                var match = this.parse_charm_id();
                if (match) {
                    return match[3] + '/' + match[4];
                }
            }
        },
        url: {},
        description: {},
        config: {},
        metadata: {},
        sha256: {}
    }
});
models.Charm = Charm;

var CharmList = Y.Base.create('charmList', Y.ModelList, [], {
    model: Charm
    }, {
    ATTRS: {
    }
});
models.CharmList = CharmList;

var Service = Y.Base.create('service', Y.Model, [], {
//    idAttribute: 'name',

    ATTRS: {
        name: {},
        charm: {},
        config: {},
        constraints: {},
        exposed: {value: false},
        relations: {},
        unit_count: {},
        aggregated_status: {}
    }
});
models.Service = Service;

var ServiceList = Y.Base.create('serviceList', Y.ModelList, [], {
    model: Service
    }, {
    ATTRS: {
    }
});
models.ServiceList = ServiceList;

var ServiceUnit = Y.Base.create('serviceUnit', Y.Model, [], {},
//    idAttribute: 'name',
    {
    ATTRS: {
        service: {
            valueFn: function(name) {
                var unit_name = this.get('id');
                return unit_name.split('/', 1)[0];
            }
        },
        number: {
            valueFn: function(name) {
                var unit_name = this.get('id');
                return parseInt(unit_name.split('/')[1], 10);
            }
        },
        machine: {},
        agent_state: {},
        // relations to unit relation state.
        relations: {},

        config: {},
        is_subordinate: {
            value: false // default
        },
        open_ports: {},
        public_address: {},
        private_address: {}
    }
});
models.ServiceUnit = ServiceUnit;

var ServiceUnitList = Y.Base.create('serviceUnitList', Y.ModelList, [], {
    model: ServiceUnit,
    get_units_for_service: function(service, asList) {
        var options = {},
            sid = service.get('id');

        if (asList !== undefined) {
            options.asList = true;
        }

        var units = this.filter(options, function(m) {
            return m.get('service') == sid;
        });
        return units;
    },
    /*
     *  Return information about the state of all units focused on the
     * 'worst' status information available. In this way if any unit is in
     *  an error state we can report that for the whole list.
     *
     * KT - Should return an aggregate count by state (map), so proproptional
     * representation can be done in various renderings.
     */
    get_informative_state: function() {
    },
    /*
     *  Return information about the state of the set of units for a
     *  given service in the form of a map of agent states:
     *  state => number of units in that state
     */
    get_informative_states_for_service: function(service) {
        var aggregate_map = {}, aggregate_list = [],
            units_for_service = this.get_units_for_service(service);

        units_for_service.forEach(function(unit) {
            var state = unit.get('agent_state');
            if (aggregate_map[state] === undefined) {
                aggregate_map[state] = 1;
            } else {
                aggregate_map[state]++;
            }

        });

        return aggregate_map;
    },

    /*
     * Updates a service's unit count and aggregate state map during a
     * delta, ensuring that they're up to date.
     */
    update_service_unit_aggregates: function(service) {
        var aggregate = this.get_informative_states_for_service(service);
        var sum = 0;
        for (var agent_state in aggregate) {
            sum += aggregate[agent_state];
        }
        service.set('unit_count', sum);
        service.set('aggregated_status', aggregate);
    },
    ATTRS: {
    }
});
models.ServiceUnitList = ServiceUnitList;

var Machine = Y.Base.create('machine', Y.Model, [], {
    idAttribute: 'machine_id'
    }, {
    ATTRS: {
        machine_id: {},
        public_address: {},
        instance_id: {},
    instance_state: {},
    agent_state: {}
    }
});
models.Machine = Machine;

var MachineList = Y.Base.create('machineList', Y.ModelList, [], {
    model: Machine
    }, {
    ATTRS: {
    }
});
models.MachineList = MachineList;

var Relation = Y.Base.create('relation', Y.Model, [], {
    idAttribute: 'relation_id'
    }, {
    ATTRS: {
        relation_id: {},
        type: {},
        endpoints: {}
    }
});
models.Relation = Relation;

var RelationList = Y.Base.create('relationList', Y.ModelList, [], {}, {
    ATTRS: {
    }
});
models.RelationList = RelationList;

var Database = Y.Base.create('database', Y.Base, [], {
    initializer: function() {
        this.services = new ServiceList();
        this.machines = new MachineList();
        this.charms = new CharmList();
        this.relations = new RelationList();

        // This one is dangerous.. we very well may not have capacity
        // to store a 1-1 representation of units in js.
        // At least we should never assume the collection is complete, and
        // have usage of some ephemeral slice/cursor of the collection.
        // Indexed db might be interesting to explore here, with object delta
        // and bulk transfer feeding directly into indexedb.
        // Needs some experimentation with a large data set.
        this.units = new ServiceUnitList();

        // For model syncing by type. Charms aren't currently sync'd, only fetched
        // on demand (they are static).
        this.model_map = {
            'unit': ServiceUnit,
            'machine': Machine,
            'service': Service,
            'relation': Relation,
            'charm': Charm
        };
    },

    reset: function() {
        this.services.reset();
        this.machines.reset();
        this.charms.reset();
        this.relations.reset();
        this.units.reset();
    },

    on_delta: function(delta_evt) {
        var changes = delta_evt.data.result;
        console.log('Delta', this, changes);
        var change_type, model_class = null, self = this;

        changes.forEach(
            Y.bind(function(change) {
                change_type = change[0];
                model_class = this.model_map[change_type];

                if (!model_class) {
                    console.log('Unknown Change', change);
                }
                console.log('change', this, change);
                var model_list = this[change_type + 's'];
                this.process_model_delta(change, model_class, model_list);
            }, this));
        this.services.each(function(service) {
            self.units.update_service_unit_aggregates(service);
        });
        this.fire('update');
    },

    // utility method to sync a data object and a model object.
    _sync_bag: function(bag, model) {
        // TODO: need to bulk mutate and then send mutation event,
        // as is this is doing per attribute events.
        for (var vid in bag) {
            var value = bag[vid];
            var aid = vid.replace('-', '_');
            if (model.get(aid) != value) {
                model.set(aid, value);
            }
        }
    },

    process_model_delta: function(change, ModelClass, model_list) {
        // console.log('model change', change);
        var change_kind = change[1];
        var data = change[2];
        var o;
        if (change_kind == 'add' || change_kind == 'change') {
            // Client-side requests may create temporary objects in the
            // database in order to give the user more immediate feedback.
            // The temporary objects are created after the ACK message from
            // the server that contains their actual names.  When the delta
            // arrives for those objects, they already exist in a skeleton
            // form that needs to be fleshed out.  So, the existing objects
            // are kept and re-used.
            o = model_list.getById(data.id);
            if (Y.Lang.isNull(o)) {
                o = new ModelClass({id: data.id});
                this._sync_bag(data, o);
                model_list.add(o);
            } else {
                this._sync_bag(data, o);
            }
        } else if (change_kind == 'remove') {
            o = model_list.getById(data);
            // This should only be necessary while we are waiting on server
            // side changes to have deltas aware of client requests.
            if (!Y.Lang.isNull(o)) {
                model_list.remove(o);
            }
        } else {
            console.log('Unknown change kind in process_model_delta: ',
                        change_kind);
        }
    }

});

models.Database = Database;

Y.namespace('juju').db = new Database({});

}, '0.1.0', {
    requires: [
        'model',
        'model-list',
        'lazy-model-list'
    ]
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var config = require('../config'),
  mongoose = require('./mongoose'),
  express = require('./express'),
  chalk = require('chalk'),
  seed = require('./seed');

function seedDB() {
  if (config.seedDB && config.seedDB.seed) {
    console.log(chalk.bold.red('Warning:  Database seeding is turned on'));
    seed.start();
  }
}

// Initialize Models
mongoose.loadModels(seedDB);

module.exports.loadModels = function loadModels() {
  mongoose.loadModels();
};

module.exports.init = function init(callback) {
  mongoose.connect(function (db) {
    // Initialize express
    var app = express.init(db);
    if (callback) callback(app, db, config);

  });
};

module.exports.start = function start(callback) {
  var _this = this;

  _this.init(function (app, db, config) {

    // Start the app by listening on <port>
    //app.listen(config.port, function () {
    app.listen(80, function () {

      // Logging initialization
      console.log('--');
      console.log(chalk.green(config.app.title));
      console.log(chalk.green('Environment:\t\t\t' + process.env.NODE_ENV));
      console.log(chalk.green('Port:\t\t\t\t' + config.port));
      console.log(chalk.green('Database:\t\t\t\t' + config.db.uri));
      if (process.env.NODE_ENV === 'secure') {
        console.log(chalk.green('HTTPs:\t\t\t\ton'));
      }
      console.log(chalk.green('App version:\t\t\t' + config.meanjs.version));
      if (config.meanjs['meanjs-version'])
        console.log(chalk.green('MEAN.JS version:\t\t\t' + config.meanjs['meanjs-version']));
      console.log('--');

      if (callback) callback(app, db, config);
    });

  });

};
<commit_msg>undo port listener<commit_after>'use strict';

/**
 * Module dependencies.
 */
var config = require('../config'),
  mongoose = require('./mongoose'),
  express = require('./express'),
  chalk = require('chalk'),
  seed = require('./seed');

function seedDB() {
  if (config.seedDB && config.seedDB.seed) {
    console.log(chalk.bold.red('Warning:  Database seeding is turned on'));
    seed.start();
  }
}

// Initialize Models
mongoose.loadModels(seedDB);

module.exports.loadModels = function loadModels() {
  mongoose.loadModels();
};

module.exports.init = function init(callback) {
  mongoose.connect(function (db) {
    // Initialize express
    var app = express.init(db);
    if (callback) callback(app, db, config);

  });
};

module.exports.start = function start(callback) {
  var _this = this;

  _this.init(function (app, db, config) {

    // Start the app by listening on <port>
    app.listen(config.port, function () {

      // Logging initialization
      console.log('--');
      console.log(chalk.green(config.app.title));
      console.log(chalk.green('Environment:\t\t\t' + process.env.NODE_ENV));
      console.log(chalk.green('Port:\t\t\t\t' + config.port));
      console.log(chalk.green('Database:\t\t\t\t' + config.db.uri));
      if (process.env.NODE_ENV === 'secure') {
        console.log(chalk.green('HTTPs:\t\t\t\ton'));
      }
      console.log(chalk.green('App version:\t\t\t' + config.meanjs.version));
      if (config.meanjs['meanjs-version'])
        console.log(chalk.green('MEAN.JS version:\t\t\t' + config.meanjs['meanjs-version']));
      console.log('--');

      if (callback) callback(app, db, config);
    });

  });

};
<|endoftext|>


<commit_before>import { PropTypes } from "react";

const Guest = ({ disabled, onClick, text }) => {
  const classes = [
    "outlined--bottom",
    "outlined--light",
  ];

  let style = {
    display: "inline",
  };

  if (disabled) {
    classes.push("text-light-tertiary");
    style = { ...style, ...{ cursor: "text" } };
  } else {
    classes.push("text-dark-tertiary");
    style = { ...style, ...{ cursor: "pointer" } };
  }

  /* eslint-disable */
  return (

        {text || "Give as Guest"}

  );

  /* eslint-enable */
};

Guest.propTypes = {
  disabled: PropTypes.bool,
  onClick: PropTypes.func,
  text: PropTypes.text,
};

export default Guest;
<commit_msg>remove lint disable on Guest button<commit_after>import { PropTypes } from "react";

const Guest = ({ disabled, onClick, text }) => {
  const classes = [
    "outlined--bottom",
    "outlined--light",
  ];

  let style = {
    display: "inline",
  };

  if (disabled) {
    classes.push("text-light-tertiary");
    style = { ...style, ...{ cursor: "text" } };
  } else {
    classes.push("text-dark-tertiary");
    style = { ...style, ...{ cursor: "pointer" } };
  }

  return (

        {text || "Give as Guest"}

  );
};

Guest.propTypes = {
  disabled: PropTypes.bool,
  onClick: PropTypes.func,
  text: PropTypes.text,
};

export default Guest;
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react'

const ListItem = ({ onClick, community: { id, name, image } }) => (

 onClick(id)} style={{ cursor: 'pointer' }}>
    <u
      className='logo-icon nossas left'
      style={{
        backgroundImage: image ? `url(${image})` : undefined,
        boxShadow: 'none'
      }}
    />

      {name}

)

ListItem.propTypes = {
  onClick: PropTypes.func.isRequired,
  community: PropTypes.shape({
    id: PropTypes.number.isRequired,
    name: PropTypes.string.isRequired,
    image: PropTypes.string.isRequired
  })
}

export default ListItem
<commit_msg>Remove required community.image in list<commit_after>import React, { PropTypes } from 'react'

const ListItem = ({ onClick, community: { id, name, image } }) => (

 onClick(id)} style={{ cursor: 'pointer' }}>
    <u
      className='logo-icon nossas left'
      style={{
        backgroundImage: image ? `url(${image})` : undefined,
        boxShadow: 'none'
      }}
    />

      {name}

)

ListItem.propTypes = {
  onClick: PropTypes.func.isRequired,
  community: PropTypes.shape({
    id: PropTypes.number.isRequired,
    name: PropTypes.string.isRequired,
    image: PropTypes.string
  })
}

export default ListItem
<|endoftext|>


<commit_before>export default {

  langName: `English`,
  langCode: `en`,

  header: {},

  footer: {
    header_v1: [
      {
        name: `__githubRepoLink__`,
        text: `Cliff Effects Tool`,
      },
    ],
    cfbCredit_v1: [
      `Made with `,
      { name: `__heartIcon__` },
      ` by Code for Boston`,
    ],
  },

  homePage: {
    appName_v1:          `Cliff Effects Tool`,
    prototypeNote_v1:    `GUIDANCE PROTOTYPE*`,
    cautionaryNote_v1:   `*This is a prototype and should not be used to make financial decisions`,
    toFirstInputPage_v1: `Get Started`,
    toAboutPage_v1:      `Learn More`,
  },

  aboutPage: {
    aboutPageHeader_v1: `About the Cliff Effects Tool`,

    whatForHeader_v1:        `What is this tool for?`,
    whatForImportantNote_v1: [
      {
        name: `__importantNote__`,
        text: `Important Note:`,
      },
      `This application is a minimum viable product. It should not be used as the sole tool to understand a client's SNAP or Section 8 financial situation, or for any other public assistance program.`,
    ],
    whatFor_v2: [
      `This tool can help show how a change in income affects how much someone receives in public assistance from SNAP (Supplemental Nutrition Assistance Program) and Section 8 Housing Voucher benefits. It was designed for the case managers at `,
      { name: `__projectHope__` },
      ` with the aim of helping to predict changes in their clients' benefits.`,
    ],

    whyHeader_v1: `Why is this tool important?`,
    why1_v1:      `A cliff effect occurs when a slight change in a household’s circumstances - say, a slight pay raise - disproportionately lowers their benefits. The household is working to increase what they earn, but they end up with a net loss that actually puts them further behind. These cliff effects prevent many families from actually getting off of public assistance programs.`,
    why2_v1:      `Cliff effects are also difficult to predict. The interactions between income, household size, many other criteria, as well as the effects of the programs themselves impact each other in unexpected ways. We're exploring ways to deal with this issue of complexity and help families better understand and predict their situation.`,

    videoLinkText_v1:    `Two-minute video describing cliff effects`,
    quantLinkText_v1:    `Quantitative scenarios demonstrating cliff effects`,
    benefitsLinkText_v1: `Breakdown of different benefits offered in MA`,

    howToUseHeader_v1: `How do I use this tool?`,
    howToUse_v1:       `Go step-by-step to add information about a client's current benefits, household, income, and other relevant information. This information will be used to predict the client's approximate benefit amount. When you reach the end, change the 'Future Income' amount to see how a change in earned income will cause a change in benefit amount. Currently, the SNAP and Section 8 Housing Voucher programs are both available. Note that predictions may not directly match up with a client’s current benefit amount. The app’s focus is the amount of change that occurs in benefits when there are changes in earned income.`,
    howToUseNote_v1:   [
      `Please note that this app does not store user data, so `,
      {
        name: `__refreshWarning__`,
        text: `if you refresh the page the data you've entered will be lost.`,
      },
      ` Each time you go through the app, it's a clean slate.`,
    ],

    whoMadeThisHeader_v1: `Who is behind this?`,
    whoMadeThis1_v1:      [
      `This application is part of a project made possible by a Boston Foundation Open Door Grant to the University of Massachusetts Boston's `,
      { name: `__centerForSocialPolicy__` },
      `, in close partnership with `,
      { name: `__projectHope__` },
      ` and `,
      { name: `__codeForBoston__` },
      `. The Center for Social Policy is the lead partner for the `,
      { name: `__onSolidGroundCoalition__` },
      `.`,
    ],
    whoMadeThis2_v1: [
      `The code base is being maintained on `,
      { name: `__github__` },
      ` by `,
      { name: `__codeForBoston__` },
      ` volunteers. For more information or to report a bug, please contact `,
      { name: `__contactEmail__` },
      `.`,
    ],
    whoMadeThis3_v1: [
      `Here's a special thank you to all the Code for Boston volunteers who brought you this application, especially `,
      { name: `__namesExceptLast__` },
      `, and `,
      { name: `__lastName__` },
      `.`,
    ],
  },

  visitPage: {
    previous_v1:  `Previous`,
    next_v1:      `Next`,
    newClient_v1: `New Client`,

    stepBar: {
      currentBenefits_v1: `Current Benefits`,
      household_v1:       `Household`,
      currentIncome_v1:   `Income`,
      currentExpenses_v1: `Expenses`,
      predictions_v1:     `Predictions`,
    },

    formHelpers: {
      weekly:   `Weekly`,
      monthly:  `Monthly`,
      yearly:   `Yearly`,
      yesLabel: `Yes`,
      noLabel:  `No`,
    },

    currentBenefits: {
      currentBenefits_v1:  `Current Benefits`,
      selectBenefits_v1:   `Select the benefits you currently receive`,
      hasSection8Label_v1: `Do you have Section 8 Housing?`,
      hasSection8Hint_v1:  `Section 8 provides rental housing assistance.`,
      hasSnapLabel_v1:     `Do you have SNAP?`,
      hasSnapHint_v1:      `SNAP provides assistance with buying food`,
    },

    household: {
      title:           `Household`,
      clarifier:       `Information about the members of your household.`,
      role:            `Role`,
      age:             `Age`,
      disabled:        `Disabled`,
      headOfHousehold: `Head of Household`,
      addMember:       `Add a member`,
      spouse:          `Spouse of Head of Household`,
      childOther:      `Child/Other Household Member`,
      previous:        `Previous`,
      next:            `Next`,
    },

    currentIncome: {
      title:                  `Current Household Income`,
      clarifier:              `Income that you collected in the past 12 months.`,
      explainSnapCalculation: `This prototype will attempt to make its own calculations for SNAP amount`,
      earnedIncome:           {
        label: `Earned income`,
        hint:  `Earned income is how much you and your family get paid from working`,
      },
      TAFDC: {
        label: `TAFDC`,
        hint:  `Transitional Aid to Families with Dependent Children provides short-term financial assistance to families with children`,
      },
      SSI: {
        label: `SSI`,
        hint:  `Supplemental Security Income is a federal program that provides financial and health care assistance to people 65 and over, or people who are blind or disabled`,
      },
      SSDI: {
        label: `SSDI`,
        hint:  `Social Security Disability Income is a federal program to help people with disabilities`,
      },
      childSupport: {
        label: `Child support recieved`,
        hint:  `Child support is money paid to you by a former spouse to help your child`,
      },
      unemployment: {
        label: `Unemployment`,
        hint:  `Unemployment benefits provide income to people who have been laid off`,
      },
      workersComp: {
        label: `Worker's compensation`,
        hint:  `Worker's Compensation provides assistance for people who have been injured on the job`,
      },
      pension: {
        label: `Pension`,
        hint:  `A pension provides income to retirees, usually from their former employers`,
      },
      socialSecurity: {
        label: `Social security`,
        hint:  `Social Security is a federal program that provides assistance to retirees`,
      },
      alimony: {
        label: `Alimony`,
        hint:  `Alimony is money paid by one spouse to the other after a divorce`,
      },
      otherIncome: {
        label: `Other income`,
        hint:  `Please note income you may have from sources that are not listed above`,
      },
      previous: `Previous`,
      next:     `Next`,
    },

    currentExpenses: {
      title:                           `Current Household Expenses`,
      unreimbursedNonMedicalChildCare: {
        sectionHeading:    `Reasonable Unreimbursed Non-Medical Child(ren) Care`,
        subheading:        `A "child" is a person 12 or younger. Don't include amounts that are paid for by other benefit programs.`,
        columnExpenseType: `Expense`,
        childDirectCare:   {
          label: `Direct care costs`,
          hint:  `How much do you pay for child care out of pocket?`,
        },
        childBeforeAndAfterSchoolCare: {
          label: `Before- and after-school care`,
          hint:  `How much do you pay for child care for times before or after school?`,
        },
        childTransportation: {
          label: `Transportation costs`,
          hint:  `How much do you pay for transportation?`,
        },
        childOtherCare: {
          label: `Other care`,
          hint:  `How much do you pay for other child care?`,
        },
        doEarnBecauseOfChildCare: `Does child care allow you to make additional income?`,
        earnedBecauseOfChildCare: `Income made possible by childcare expenses`,
      },
      childSupport: {
        sectionHeading:      `Child Support`,
        columnExpenseType:   `Expense`,
        childSupportPaidOut: {
          legallyObligated: `Legally obligated`,
          childSupport:     ` child support`,
        },
      },
      housing: {
        sectionHeading:      `Housing`,
        monthlyContractRent: {
          label: `Monthly Contract Rent (the total rent for your apartment)`,
          hint:  `The total rent for your apartment`,
        },
        monthlyRentShare: {
          label: `Your Monthly Rent Share (how much of the total rent you have to pay)`,
          hint:  `How much of the total rent you have to pay`,
        },
        utilitiesSubheading: `Which of these utilities do you pay for?`,
        climateControl:      {
          label: `Heating or cooling (e.g. A/C during summer)`,
          hint:  `How much do you pay if you have a separate bill for heating and/or cooling`,
        },
        nonHeatElectricity: {
          label: `Electricity for non-heating purposes`,
          hint:  `How much do you pay for any electric usage (other than for heat)`,
        },
        phone: {
          label: `Telephone service`,
          hint:  `How much do you pay for basic telephone service`,
        },
        fuelAssistance: {
          labelText: `Do you get Fuel Assistance?`,
          hint:      `Fuel Assistance helps you pay for heating fuel`,
        },
      },
      previous: `Previous`,
      next:     `Next`,
    },

    predictions: {
      title_v1:                   `What Might Happen?`,
      futureIncomeQuestion_v1:    `How much money would you get paid in the future? (You can try different amounts)`,
      tabTitleChanges_v1:         `Changes`, // see Predictions.js
      tabTitleChangesChart_v1:    `Changes Chart`, // see Predictions.js
      tabTitleStackedIncomes_v1:  `Stacked Incomes`, // see Predictions.js
      tabTitleBenefitPrograms_v1: `Benefit Programs`, // see Predictions.js
      chartsHeader_v1:            `With the new pay, how could your benefits change?`,
      warningMessage_v1:          `This tool is in testing and these numbers might not be right. If they're not, we'd appreciate your feedback.`,
      submitFeedback_v1:          `Submit Feedback`,
      // added the following snippets Friday 2018_9_7
      // Benefits Table:
      benefitsTableTitle_v1:      `Changes`, // see line 265 above
      columnBenefit_v1:           `Benefit`, // see BenefitsTable.js
      columnCurrentBenefits_v1:   `Current Benefits`, // see BenefitsTable.js
      columnNewEstimate_v1:       `New Estimate`, // see BenefitsTable.js
      columnDifference_v1:        `Difference`, // see BenefitsTable.js
      rowSNAP_v1:                 `SNAP`, // see BenefitsTable.js
      rowSection8_v1:             `Section 8 Housing`, // see BenefitsTable.js
      rowTotalBenefits_v1:        `Total Benefits`, // see BenefitsTable.js
      rowIncome_v1:               `Income`, // see BenefitsTable.js
      rowNetTotal_v1:             `Net Total`, // see BenefitsTable.js
      beforeMoney_v1:             `$`, // see BenefitsTable.js multiple lines
      afterMoney_v1:              ` / month`, // see BenefitsTable.js multiple lines
      // Stacked Bar Graph:
      stackedBarGraphTitle_v1:    `Changes Chart`, // see line 266 above
      moneyInAsIncomeChanges_v1:  `Money Coming In as Income Changes`, // see StackedBarGraph.js
      // Stacked Area Graph:
      stackedAreaGraphTitle_v1:   `Stacked Incomes`, // see line 267 above
      allMoneyComingIn_v1:        `All Money Coming In as Income Changes`, // see StackedAreaGraph.js
      // Benefit Programs Graph, showing benefit programs as lines not areas:
      benefitProgramsTitle_v1:    `Individual Benefit Amounts for Household as Income Changes`, // maybe rename as benefitProgramsSubTitle_v1 ? see also BenefitsLineGraph.js
      benefitValue_v1:            `Benefit Value ($)`, // see BenefitsLineGraph.js
      // Snippets that are used in more than one chart, table, or graph:
      totalMoney_v1:              `Total Money Coming In ($)`, // see StackedAreaGraph.js, StackedBarGraph.js
      weeklyIncome_v1:            `Weekly Income ($)`,
      monthlyIncome_v1:           `Monthly Income ($)`,
      yearlyIncome_v1:            `Yearly Income ($)`,
      hasIncome_v1:               `Income`,
      hasSNAP_v1:                 `SNAP`,
      hasSection8_v1:             `Section 8`,
      futureIncomeLine_v1:        `Future Income`, // see VerticalLine.js
      buttonWeekly_v1:            `Weekly`, // see cashflow.js, GraphTimeButtons
      buttonMonthly_v1:           `Monthly`, // see cashflow.js, GraphTimeButtons
      buttonYearly_v1:            `Yearly`, // see cashflow.js, GraphTimeButtons
    },
  },
};
<commit_msg>Adds version numbers to Household section snippets<commit_after>export default {

  langName: `English`,
  langCode: `en`,

  header: {},

  footer: {
    header_v1: [
      {
        name: `__githubRepoLink__`,
        text: `Cliff Effects Tool`,
      },
    ],
    cfbCredit_v1: [
      `Made with `,
      { name: `__heartIcon__` },
      ` by Code for Boston`,
    ],
  },

  homePage: {
    appName_v1:          `Cliff Effects Tool`,
    prototypeNote_v1:    `GUIDANCE PROTOTYPE*`,
    cautionaryNote_v1:   `*This is a prototype and should not be used to make financial decisions`,
    toFirstInputPage_v1: `Get Started`,
    toAboutPage_v1:      `Learn More`,
  },

  aboutPage: {
    aboutPageHeader_v1: `About the Cliff Effects Tool`,

    whatForHeader_v1:        `What is this tool for?`,
    whatForImportantNote_v1: [
      {
        name: `__importantNote__`,
        text: `Important Note:`,
      },
      `This application is a minimum viable product. It should not be used as the sole tool to understand a client's SNAP or Section 8 financial situation, or for any other public assistance program.`,
    ],
    whatFor_v2: [
      `This tool can help show how a change in income affects how much someone receives in public assistance from SNAP (Supplemental Nutrition Assistance Program) and Section 8 Housing Voucher benefits. It was designed for the case managers at `,
      { name: `__projectHope__` },
      ` with the aim of helping to predict changes in their clients' benefits.`,
    ],

    whyHeader_v1: `Why is this tool important?`,
    why1_v1:      `A cliff effect occurs when a slight change in a household’s circumstances - say, a slight pay raise - disproportionately lowers their benefits. The household is working to increase what they earn, but they end up with a net loss that actually puts them further behind. These cliff effects prevent many families from actually getting off of public assistance programs.`,
    why2_v1:      `Cliff effects are also difficult to predict. The interactions between income, household size, many other criteria, as well as the effects of the programs themselves impact each other in unexpected ways. We're exploring ways to deal with this issue of complexity and help families better understand and predict their situation.`,

    videoLinkText_v1:    `Two-minute video describing cliff effects`,
    quantLinkText_v1:    `Quantitative scenarios demonstrating cliff effects`,
    benefitsLinkText_v1: `Breakdown of different benefits offered in MA`,

    howToUseHeader_v1: `How do I use this tool?`,
    howToUse_v1:       `Go step-by-step to add information about a client's current benefits, household, income, and other relevant information. This information will be used to predict the client's approximate benefit amount. When you reach the end, change the 'Future Income' amount to see how a change in earned income will cause a change in benefit amount. Currently, the SNAP and Section 8 Housing Voucher programs are both available. Note that predictions may not directly match up with a client’s current benefit amount. The app’s focus is the amount of change that occurs in benefits when there are changes in earned income.`,
    howToUseNote_v1:   [
      `Please note that this app does not store user data, so `,
      {
        name: `__refreshWarning__`,
        text: `if you refresh the page the data you've entered will be lost.`,
      },
      ` Each time you go through the app, it's a clean slate.`,
    ],

    whoMadeThisHeader_v1: `Who is behind this?`,
    whoMadeThis1_v1:      [
      `This application is part of a project made possible by a Boston Foundation Open Door Grant to the University of Massachusetts Boston's `,
      { name: `__centerForSocialPolicy__` },
      `, in close partnership with `,
      { name: `__projectHope__` },
      ` and `,
      { name: `__codeForBoston__` },
      `. The Center for Social Policy is the lead partner for the `,
      { name: `__onSolidGroundCoalition__` },
      `.`,
    ],
    whoMadeThis2_v1: [
      `The code base is being maintained on `,
      { name: `__github__` },
      ` by `,
      { name: `__codeForBoston__` },
      ` volunteers. For more information or to report a bug, please contact `,
      { name: `__contactEmail__` },
      `.`,
    ],
    whoMadeThis3_v1: [
      `Here's a special thank you to all the Code for Boston volunteers who brought you this application, especially `,
      { name: `__namesExceptLast__` },
      `, and `,
      { name: `__lastName__` },
      `.`,
    ],
  },

  visitPage: {
    previous_v1:  `Previous`,
    next_v1:      `Next`,
    newClient_v1: `New Client`,

    stepBar: {
      currentBenefits_v1: `Current Benefits`,
      household_v1:       `Household`,
      currentIncome_v1:   `Income`,
      currentExpenses_v1: `Expenses`,
      predictions_v1:     `Predictions`,
    },

    formHelpers: {
      weekly:   `Weekly`,
      monthly:  `Monthly`,
      yearly:   `Yearly`,
      yesLabel: `Yes`,
      noLabel:  `No`,
    },

    currentBenefits: {
      currentBenefits_v1:  `Current Benefits`,
      selectBenefits_v1:   `Select the benefits you currently receive`,
      hasSection8Label_v1: `Do you have Section 8 Housing?`,
      hasSection8Hint_v1:  `Section 8 provides rental housing assistance.`,
      hasSnapLabel_v1:     `Do you have SNAP?`,
      hasSnapHint_v1:      `SNAP provides assistance with buying food`,
    },

    household: {
      title:           `Household`,
      clarifier:       `Information about the members of your household.`,
      role:            `Role`,
      age:             `Age`,
      disabled:        `Disabled`,
      headOfHousehold: `Head of Household`,
      addMember:       `Add a member`,
      spouse:          `Spouse of Head of Household`,
      childOther:      `Child/Other Household Member`,
      previous:        `Previous`,
      next:            `Next`,
    },

    currentIncome: {
      title_v1:                  `Current Household Income`,
      clarifier_v1:              `Income that you collected in the past 12 months.`,
      explainSnapCalculation_v1: `This prototype will attempt to make its own calculations for SNAP amount`,
      earnedIncome:              {
        label_v1: `Earned income`,
        hint_v1:  `Earned income is how much you and your family get paid from working`,
      },
      TAFDC: {
        label_v1: `TAFDC`,
        hint_v1:  `Transitional Aid to Families with Dependent Children provides short-term financial assistance to families with children`,
      },
      SSI: {
        label_v1: `SSI`,
        hint_v1:  `Supplemental Security Income is a federal program that provides financial and health care assistance to people 65 and over, or people who are blind or disabled`,
      },
      SSDI: {
        label_v1: `SSDI`,
        hint_v1:  `Social Security Disability Income is a federal program to help people with disabilities`,
      },
      childSupport: {
        label_v1: `Child support recieved`,
        hint_v1:  `Child support is money paid to you by a former spouse to help your child`,
      },
      unemployment: {
        label_v1: `Unemployment`,
        hint_v1:  `Unemployment benefits provide income to people who have been laid off`,
      },
      workersComp: {
        label_v1: `Worker's compensation`,
        hint_v1:  `Worker's Compensation provides assistance for people who have been injured on the job`,
      },
      pension: {
        label_v1: `Pension`,
        hint_v1:  `A pension provides income to retirees, usually from their former employers`,
      },
      socialSecurity: {
        label_v1: `Social security`,
        hint_v1:  `Social Security is a federal program that provides assistance to retirees`,
      },
      alimony: {
        label_v1: `Alimony`,
        hint_v1:  `Alimony is money paid by one spouse to the other after a divorce`,
      },
      otherIncome: {
        label_v1: `Other income`,
        hint_v1:  `Please note income you may have from sources that are not listed above`,
      },
      previous_v1: `Previous`,
      next_v1:     `Next`,
    },

    currentExpenses: {
      title:                           `Current Household Expenses`,
      unreimbursedNonMedicalChildCare: {
        sectionHeading:    `Reasonable Unreimbursed Non-Medical Child(ren) Care`,
        subheading:        `A "child" is a person 12 or younger. Don't include amounts that are paid for by other benefit programs.`,
        columnExpenseType: `Expense`,
        childDirectCare:   {
          label: `Direct care costs`,
          hint:  `How much do you pay for child care out of pocket?`,
        },
        childBeforeAndAfterSchoolCare: {
          label: `Before- and after-school care`,
          hint:  `How much do you pay for child care for times before or after school?`,
        },
        childTransportation: {
          label: `Transportation costs`,
          hint:  `How much do you pay for transportation?`,
        },
        childOtherCare: {
          label: `Other care`,
          hint:  `How much do you pay for other child care?`,
        },
        doEarnBecauseOfChildCare: `Does child care allow you to make additional income?`,
        earnedBecauseOfChildCare: `Income made possible by childcare expenses`,
      },
      childSupport: {
        sectionHeading:      `Child Support`,
        columnExpenseType:   `Expense`,
        childSupportPaidOut: {
          legallyObligated: `Legally obligated`,
          childSupport:     ` child support`,
        },
      },
      housing: {
        sectionHeading:      `Housing`,
        monthlyContractRent: {
          label: `Monthly Contract Rent (the total rent for your apartment)`,
          hint:  `The total rent for your apartment`,
        },
        monthlyRentShare: {
          label: `Your Monthly Rent Share (how much of the total rent you have to pay)`,
          hint:  `How much of the total rent you have to pay`,
        },
        utilitiesSubheading: `Which of these utilities do you pay for?`,
        climateControl:      {
          label: `Heating or cooling (e.g. A/C during summer)`,
          hint:  `How much do you pay if you have a separate bill for heating and/or cooling`,
        },
        nonHeatElectricity: {
          label: `Electricity for non-heating purposes`,
          hint:  `How much do you pay for any electric usage (other than for heat)`,
        },
        phone: {
          label: `Telephone service`,
          hint:  `How much do you pay for basic telephone service`,
        },
        fuelAssistance: {
          labelText: `Do you get Fuel Assistance?`,
          hint:      `Fuel Assistance helps you pay for heating fuel`,
        },
      },
      previous: `Previous`,
      next:     `Next`,
    },

    predictions: {
      title_v1:                   `What Might Happen?`,
      futureIncomeQuestion_v1:    `How much money would you get paid in the future? (You can try different amounts)`,
      tabTitleChanges_v1:         `Changes`, // see Predictions.js
      tabTitleChangesChart_v1:    `Changes Chart`, // see Predictions.js
      tabTitleStackedIncomes_v1:  `Stacked Incomes`, // see Predictions.js
      tabTitleBenefitPrograms_v1: `Benefit Programs`, // see Predictions.js
      chartsHeader_v1:            `With the new pay, how could your benefits change?`,
      warningMessage_v1:          `This tool is in testing and these numbers might not be right. If they're not, we'd appreciate your feedback.`,
      submitFeedback_v1:          `Submit Feedback`,
      // added the following snippets Friday 2018_9_7
      // Benefits Table:
      benefitsTableTitle_v1:      `Changes`, // see line 265 above
      columnBenefit_v1:           `Benefit`, // see BenefitsTable.js
      columnCurrentBenefits_v1:   `Current Benefits`, // see BenefitsTable.js
      columnNewEstimate_v1:       `New Estimate`, // see BenefitsTable.js
      columnDifference_v1:        `Difference`, // see BenefitsTable.js
      rowSNAP_v1:                 `SNAP`, // see BenefitsTable.js
      rowSection8_v1:             `Section 8 Housing`, // see BenefitsTable.js
      rowTotalBenefits_v1:        `Total Benefits`, // see BenefitsTable.js
      rowIncome_v1:               `Income`, // see BenefitsTable.js
      rowNetTotal_v1:             `Net Total`, // see BenefitsTable.js
      beforeMoney_v1:             `$`, // see BenefitsTable.js multiple lines
      afterMoney_v1:              ` / month`, // see BenefitsTable.js multiple lines
      // Stacked Bar Graph:
      stackedBarGraphTitle_v1:    `Changes Chart`, // see line 266 above
      moneyInAsIncomeChanges_v1:  `Money Coming In as Income Changes`, // see StackedBarGraph.js
      // Stacked Area Graph:
      stackedAreaGraphTitle_v1:   `Stacked Incomes`, // see line 267 above
      allMoneyComingIn_v1:        `All Money Coming In as Income Changes`, // see StackedAreaGraph.js
      // Benefit Programs Graph, showing benefit programs as lines not areas:
      benefitProgramsTitle_v1:    `Individual Benefit Amounts for Household as Income Changes`, // maybe rename as benefitProgramsSubTitle_v1 ? see also BenefitsLineGraph.js
      benefitValue_v1:            `Benefit Value ($)`, // see BenefitsLineGraph.js
      // Snippets that are used in more than one chart, table, or graph:
      totalMoney_v1:              `Total Money Coming In ($)`, // see StackedAreaGraph.js, StackedBarGraph.js
      weeklyIncome_v1:            `Weekly Income ($)`,
      monthlyIncome_v1:           `Monthly Income ($)`,
      yearlyIncome_v1:            `Yearly Income ($)`,
      hasIncome_v1:               `Income`,
      hasSNAP_v1:                 `SNAP`,
      hasSection8_v1:             `Section 8`,
      futureIncomeLine_v1:        `Future Income`, // see VerticalLine.js
      buttonWeekly_v1:            `Weekly`, // see cashflow.js, GraphTimeButtons
      buttonMonthly_v1:           `Monthly`, // see cashflow.js, GraphTimeButtons
      buttonYearly_v1:            `Yearly`, // see cashflow.js, GraphTimeButtons
    },
  },
};
<|endoftext|>


<commit_before>/* global Module */

/* Magic Mirror
 * Module: MMM-Departures
 *
 * By Bengt Giger
 * MIT Licensed.
 */

Module.register('MMM-Departures', {

  defaults: {
    apiUrl: 'http://transportrest-sbiermann.rhcloud.com/departureFHEM',
    absoluteTime: true,
    maxElements: 5,
    initialLoadDelay: 1000,
    updateInterval: 30 * 60 * 1000, // every 30 minutes
    debug: false,
  },

  getStyles: function () {
    return ["MMM-Departures.css"];
  },

  // Method is called when all modules are loaded an the system is ready to boot up
  start: function() {
    var self = this;
    Log.info('Starting module: ' + this.name);
    this.updateTimer = null;
    this.lastUpdate = NaN;

    this.scheduleUpdate(this.config.initialLoadDelay);

    setInterval(function() {
      self.updateDom();
    }, 30000);
  },

  /* scheduleUpdate()
   * Schedule next update.
   *
   * argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
   */
  scheduleUpdate: function (delay) {
    var nextLoad = this.config.updateInterval;
    if (typeof delay !== 'undefined' && delay >= 0) {
      nextLoad = delay;
    }

    var self = this;
    clearTimeout(this.updateTimer);
    // Timer to fetch new data
    this.updateTimer = setTimeout(function () {
      self.sendSocketNotification('GETDATA', self.config);
      // Log.log('Departures new data fetched...');
    }, nextLoad);
  },

  // Override socket notification handler.
  // Module notifications from node_helper
  socketNotificationReceived: function(notification, payload) {
    if (notification === 'DATARECEIVED') {
      for (var i = 0; i < this.config.stations.length; i++){
        if (this.config.stations[i].stationId == payload.id) {
          this.config.stations[i].departures = payload.departures;
          this.loaded = true;
        }
      }
      this.lastUpdate = Date.now();
      this.updateDom();
      this.scheduleUpdate(this.config.updateInterval);
    }
  },

  leadingZero: function(num) {
    num = (num < 10 ? '0' : '' )+ num;
    return num;
  },

  // Filter departures
  filterPassedDepartures: function(departures, minutesSinceUpdate, threshold) {
    var filteredDepartures = [];

    for (var i = 0; i < departures.length; i++) {
      if (parseInt(departures[i][2], 10) - minutesSinceUpdate > 0) {
        if (parseInt(departures[i][2], 10) - minutesSinceUpdate >= threshold) {
          filteredDepartures.push(station.departures[i]);
        }
      }
    }
    return filteredDepartures;
  },
  filterIncludedLines: function(departures, wantedLines ) {
    var filteredDepartures = [];
    for (var i = 0; i < departures.length; i++) {
      for (var j = 0; j < wantedLines.length; j++) {
        if (wantedLines[j] == departures[i][0]) {
          filteredDepartures.push(departures[i]);
        }
      }
    }
    return filteredDepartures;
  },
  filterExcludedLines: function(departures, unwantedLines ) {
    var filteredDepartures = [];
    for (var i = 0; i < departures.length; i++) {
      for (var j = 0; j < unwantedLines.length; j++) {
        if (unwantedLines[j] != departures[i][0]) {
          filteredDepartures.push(departures[i]);
        }
      }
    }
    return filteredDepartures;
  },
  filterDepartures: function(departures, station, minutesSinceUpdate) {
    if (station.hasOwnProperty("hideBelow")) {
      departures = this.filterPassedDepartures(departures, minutesSinceUpdate, station.hideBelow);
    } else {
      departures = this.filterPassedDepartures(departures, minutesSinceUpdate, 0);
    }
    // Filter wanted/unwanted lines
    if (station.hasOwnProperty("includeLines")) {
      departures = this.filterIncludedLines(departures, station.includeLines);
    }
    if (station.hasOwnProperty("excludeLines")) {
      departures = this.filterExcludedLines(departures, station.excludeLines);
    }
    return departures;
  },

  // Update the information on screen
  getDom: function() {
    var self = this;

    // global config check
    if ( ! this.config.hasOwnProperty("provider")) {
      var errorWrapper = document.createElement("div");
      errorWrapper.className = "small";
      errorWrapper.innerHTML = "MMM-Departures: No provider set";
      return errorWrapper;
    }
    if ( ! this.config.hasOwnProperty("stations")) {
      var errorWrapper = document.createElement("div");
      errorWrapper.className = "small";
      errorWrapper.innerHTML = "MMM-Departures: No station set";
      return errorWrapper;
    }

    if (this.config.debug) { Log.log("Stations defined: " + this.config.stations.length) };

    if (!this.loaded) {
      if (this.config.debug) { Log.log("State: module not completely loaded yet") };
      var wrapper = document.createElement("div");
      wrapper.innerHTML = this.translate("LOADING");
      wrapper.className = "small dimmed";
      return wrapper;
    }

    var wrapper = document.createElement("table");
    wrapper.className = "small";

    // Now iterate over defined stations
    for (var si = 0; si < this.config.stations.length; si++) {
      station = this.config.stations[si];
      if (this.config.debug) { Log.log("Processing station " + station.stationName) };

      // station config check
      if (station.hasOwnProperty("includeLines") && station.hasOwnProperty("excludeLines")) {
        var stationRowWrapper = document.createElement("tr");
        var stationWrapper = document.createElement("td");
        stationWrapper.setAttribute("colspan", "3");
        stationWrapper.innerHTML = station.stationName + ": only one of 'includeLines' and 'excludeLines' allowed";
        stationRowWrapper.appendChild(stationWrapper);
        wrapper.append(stationRowWrapper);
        if (this.config.debug) { Log.log("- Processing stopped: both 'includeLines' and 'excludeLines' used") };
        break;
      }

      var minutesSinceUpdate = (Date.now() - this.lastUpdate)/60000;
      // Process only stations with departures
      if (station.hasOwnProperty("departures") && station.departures.length > 0) {

        // Filter departures, eliminate if too late to reach or even passed
        var activeDepartures = station.departures;
        Log.log("Minutes since update " + minutesSinceUpdate);
        activeDepartures = this.filterDepartures(activeDepartures, station, minutesSinceUpdate);

        // station may be not ready yet during startup
        try {
          var departuresShown = Math.min(this.config.maxElements, station.departures.length);
        } catch (e) {
          departuresShown = this.config.maxElements;
        }

        if (this.config.debug) { Log.log("- Departures found: " + departuresShown) };

        // Build departure table: header with station name
        var stationRowNameWrapper = document.createElement("tr");
        // stationRowNameWrapper.className = "small";

        var stationNameWrapper = document.createElement("td");
        //stationNameWrapper.className = "bright";
        stationNameWrapper.className = "heading";
        stationNameWrapper.setAttribute("colspan", "3");
        stationNameWrapper.innerHTML = station.stationName;

        var stationRowWrapper = document.createElement("tr");
        stationRowWrapper.appendChild(stationNameWrapper);
        stationRowWrapper.appendChild(document.createElement("td"));
        stationRowWrapper.appendChild(document.createElement("td"));
        wrapper.appendChild(stationRowWrapper);

        // Now list departures
        for (var i = 0; i < departuresShown; i++) {
          if (this.config.debug) { Log.log("-- Processing departure: " + activeDepartures[i]) };
          var departureWrapper = document.createElement("tr");
          // Fade out last 2 entries (only if more than 2 entries...)
          if (departuresShown > 2) {
            if (i == departuresShown - 2) {
              departureWrapper.setAttribute("style", "opacity: 0.777777");
            } else if (i == departuresShown - 1) {
              departureWrapper.setAttribute("style", "opacity: 0.444444");
            }
          }

          // Split departure info into table fields
          for (var j = 0; j < 3; j++) {
            var departureInfoWrapper = document.createElement("td");
            // first 2 fields with spacer
            if (j < 2) {
              // cut destination station if configured
              if (j == 1 && this.config.hasOwnProperty("maxDestinationLength")) {
                departureInfoWrapper.innerHTML = activeDepartures[i][j].substr(0, this.config.maxDestinationLength) + " ";
              } else {
                departureInfoWrapper.innerHTML = activeDepartures[i][j] + " ";
              }
              departureInfoWrapper.className = "align-left";
            }
            // time field
            if (j == 2) {
              if (this.config.absoluteTime) {
                // Calculate absolute time
                var now = new Date();
                var departureTime = new Date();
                departureTime.setTime(now.getTime() + parseInt(activeDepartures[i][j],10)*1000*60)
                departureInfoWrapper.innerHTML = departureTime.getHours() + ":" + this.leadingZero(departureTime.getMinutes());
              } else {
                // Correction for relative time since some time passed since data update
                departureInfoWrapper.innerHTML = parseInt(parseInt(activeDepartures[i][j],10)-minutesSinceUpdate)+"m";
              }
              departureInfoWrapper.className = "align-right";
            }
            departureWrapper.appendChild(departureInfoWrapper);
          }

          wrapper.appendChild(departureWrapper);
        }
      }
    }

    return wrapper;
  },

});
<commit_msg>Logging was left, removed<commit_after>/* global Module */

/* Magic Mirror
 * Module: MMM-Departures
 *
 * By Bengt Giger
 * MIT Licensed.
 */

Module.register('MMM-Departures', {

  defaults: {
    apiUrl: 'http://transportrest-sbiermann.rhcloud.com/departureFHEM',
    absoluteTime: true,
    maxElements: 5,
    initialLoadDelay: 1000,
    updateInterval: 30 * 60 * 1000, // every 30 minutes
    debug: false,
  },

  getStyles: function () {
    return ["MMM-Departures.css"];
  },

  // Method is called when all modules are loaded an the system is ready to boot up
  start: function() {
    var self = this;
    Log.info('Starting module: ' + this.name);
    this.updateTimer = null;
    this.lastUpdate = NaN;

    this.scheduleUpdate(this.config.initialLoadDelay);

    setInterval(function() {
      self.updateDom();
    }, 30000);
  },

  /* scheduleUpdate()
   * Schedule next update.
   *
   * argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
   */
  scheduleUpdate: function (delay) {
    var nextLoad = this.config.updateInterval;
    if (typeof delay !== 'undefined' && delay >= 0) {
      nextLoad = delay;
    }

    var self = this;
    clearTimeout(this.updateTimer);
    // Timer to fetch new data
    this.updateTimer = setTimeout(function () {
      self.sendSocketNotification('GETDATA', self.config);
      // Log.log('Departures new data fetched...');
    }, nextLoad);
  },

  // Override socket notification handler.
  // Module notifications from node_helper
  socketNotificationReceived: function(notification, payload) {
    if (notification === 'DATARECEIVED') {
      for (var i = 0; i < this.config.stations.length; i++){
        if (this.config.stations[i].stationId == payload.id) {
          this.config.stations[i].departures = payload.departures;
          this.loaded = true;
        }
      }
      this.lastUpdate = Date.now();
      this.updateDom();
      this.scheduleUpdate(this.config.updateInterval);
    }
  },

  leadingZero: function(num) {
    num = (num < 10 ? '0' : '' )+ num;
    return num;
  },

  // Filter departures
  filterPassedDepartures: function(departures, minutesSinceUpdate, threshold) {
    var filteredDepartures = [];

    for (var i = 0; i < departures.length; i++) {
      if (parseInt(departures[i][2], 10) - minutesSinceUpdate > 0) {
        if (parseInt(departures[i][2], 10) - minutesSinceUpdate >= threshold) {
          filteredDepartures.push(station.departures[i]);
        }
      }
    }
    return filteredDepartures;
  },
  filterIncludedLines: function(departures, wantedLines ) {
    var filteredDepartures = [];
    for (var i = 0; i < departures.length; i++) {
      for (var j = 0; j < wantedLines.length; j++) {
        if (wantedLines[j] == departures[i][0]) {
          filteredDepartures.push(departures[i]);
        }
      }
    }
    return filteredDepartures;
  },
  filterExcludedLines: function(departures, unwantedLines ) {
    var filteredDepartures = [];
    for (var i = 0; i < departures.length; i++) {
      for (var j = 0; j < unwantedLines.length; j++) {
        if (unwantedLines[j] != departures[i][0]) {
          filteredDepartures.push(departures[i]);
        }
      }
    }
    return filteredDepartures;
  },
  filterDepartures: function(departures, station, minutesSinceUpdate) {
    if (station.hasOwnProperty("hideBelow")) {
      departures = this.filterPassedDepartures(departures, minutesSinceUpdate, station.hideBelow);
    } else {
      departures = this.filterPassedDepartures(departures, minutesSinceUpdate, 0);
    }
    // Filter wanted/unwanted lines
    if (station.hasOwnProperty("includeLines")) {
      departures = this.filterIncludedLines(departures, station.includeLines);
    }
    if (station.hasOwnProperty("excludeLines")) {
      departures = this.filterExcludedLines(departures, station.excludeLines);
    }
    return departures;
  },

  // Update the information on screen
  getDom: function() {
    var self = this;

    // global config check
    if ( ! this.config.hasOwnProperty("provider")) {
      var errorWrapper = document.createElement("div");
      errorWrapper.className = "small";
      errorWrapper.innerHTML = "MMM-Departures: No provider set";
      return errorWrapper;
    }
    if ( ! this.config.hasOwnProperty("stations")) {
      var errorWrapper = document.createElement("div");
      errorWrapper.className = "small";
      errorWrapper.innerHTML = "MMM-Departures: No station set";
      return errorWrapper;
    }

    if (this.config.debug) { Log.log("Stations defined: " + this.config.stations.length) };

    if (!this.loaded) {
      if (this.config.debug) { Log.log("State: module not completely loaded yet") };
      var wrapper = document.createElement("div");
      wrapper.innerHTML = this.translate("LOADING");
      wrapper.className = "small dimmed";
      return wrapper;
    }

    var wrapper = document.createElement("table");
    wrapper.className = "small";

    // Now iterate over defined stations
    for (var si = 0; si < this.config.stations.length; si++) {
      station = this.config.stations[si];
      if (this.config.debug) { Log.log("Processing station " + station.stationName) };

      // station config check
      if (station.hasOwnProperty("includeLines") && station.hasOwnProperty("excludeLines")) {
        var stationRowWrapper = document.createElement("tr");
        var stationWrapper = document.createElement("td");
        stationWrapper.setAttribute("colspan", "3");
        stationWrapper.innerHTML = station.stationName + ": only one of 'includeLines' and 'excludeLines' allowed";
        stationRowWrapper.appendChild(stationWrapper);
        wrapper.append(stationRowWrapper);
        if (this.config.debug) { Log.log("- Processing stopped: both 'includeLines' and 'excludeLines' used") };
        break;
      }

      var minutesSinceUpdate = (Date.now() - this.lastUpdate)/60000;
      // Process only stations with departures
      if (station.hasOwnProperty("departures") && station.departures.length > 0) {

        // Filter departures, eliminate if too late to reach or even passed
        var activeDepartures = station.departures;
        activeDepartures = this.filterDepartures(activeDepartures, station, minutesSinceUpdate);

        // station may be not ready yet during startup
        try {
          var departuresShown = Math.min(this.config.maxElements, station.departures.length);
        } catch (e) {
          departuresShown = this.config.maxElements;
        }

        if (this.config.debug) { Log.log("- Departures found: " + departuresShown) };

        // Build departure table: header with station name
        var stationRowNameWrapper = document.createElement("tr");
        // stationRowNameWrapper.className = "small";

        var stationNameWrapper = document.createElement("td");
        //stationNameWrapper.className = "bright";
        stationNameWrapper.className = "heading";
        stationNameWrapper.setAttribute("colspan", "3");
        stationNameWrapper.innerHTML = station.stationName;

        var stationRowWrapper = document.createElement("tr");
        stationRowWrapper.appendChild(stationNameWrapper);
        stationRowWrapper.appendChild(document.createElement("td"));
        stationRowWrapper.appendChild(document.createElement("td"));
        wrapper.appendChild(stationRowWrapper);

        // Now list departures
        for (var i = 0; i < departuresShown; i++) {
          if (this.config.debug) { Log.log("-- Processing departure: " + activeDepartures[i]) };
          var departureWrapper = document.createElement("tr");
          // Fade out last 2 entries (only if more than 2 entries...)
          if (departuresShown > 2) {
            if (i == departuresShown - 2) {
              departureWrapper.setAttribute("style", "opacity: 0.777777");
            } else if (i == departuresShown - 1) {
              departureWrapper.setAttribute("style", "opacity: 0.444444");
            }
          }

          // Split departure info into table fields
          for (var j = 0; j < 3; j++) {
            var departureInfoWrapper = document.createElement("td");
            // first 2 fields with spacer
            if (j < 2) {
              // cut destination station if configured
              if (j == 1 && this.config.hasOwnProperty("maxDestinationLength")) {
                departureInfoWrapper.innerHTML = activeDepartures[i][j].substr(0, this.config.maxDestinationLength) + " ";
              } else {
                departureInfoWrapper.innerHTML = activeDepartures[i][j] + " ";
              }
              departureInfoWrapper.className = "align-left";
            }
            // time field
            if (j == 2) {
              if (this.config.absoluteTime) {
                // Calculate absolute time
                var now = new Date();
                var departureTime = new Date();
                departureTime.setTime(now.getTime() + parseInt(activeDepartures[i][j],10)*1000*60)
                departureInfoWrapper.innerHTML = departureTime.getHours() + ":" + this.leadingZero(departureTime.getMinutes());
              } else {
                // Correction for relative time since some time passed since data update
                departureInfoWrapper.innerHTML = parseInt(parseInt(activeDepartures[i][j],10)-minutesSinceUpdate)+"m";
              }
              departureInfoWrapper.className = "align-right";
            }
            departureWrapper.appendChild(departureInfoWrapper);
          }

          wrapper.appendChild(departureWrapper);
        }
      }
    }

    return wrapper;
  },

});
<|endoftext|>


<commit_before>define([
    'dojo/_base/declare',
    'dojo/_base/lang',
    'dojo/Deferred',
    'dojo/dom-construct',
    'dojo/query',
    'JBrowse/View/Track/HTMLFeatures'
],
function (
    declare,
    lang,
    Deferred,
    domConstruct,
    query,
    HTMLFeatures
) {
    return declare(HTMLFeatures, {
        renderFeature: function (feature, uniqueId, block, scale, labelScale, descriptionScale, containerStart, containerEnd) {
            var featureNode = this.inherited(arguments);
            var thisB = this;

            thisB.insertIntrons(featureNode);
            thisB.paintNeatFeatures(featureNode);
            return featureNode;
        },
        insertIntrons: function (featureNode) {
            // ignore if we have already processed this node
            if (!dojo.hasClass(featureNode, 'has-neat-introns')) {
                // get the subfeatures nodes (only immediate children)
                var subNodesX = query('> .subfeature', featureNode);

                // filter nodes - eliminate nodes that are splice sites (for Apollo)
                var subNodes = [];
                for (var i = 0; i < subNodesX.length; i++) {
                    var attr = dojo.attr(subNodesX[i], 'class');
                    if (attr.indexOf('splice-site') === -1  && attr.indexOf('stop_codon_read_through') === -1 && attr.indexOf('intron') === -1) {
                        subNodes.push(subNodesX[i]);
                    }
                }
                if (subNodes.length < 2) {
                    // apply introns to all feature tracks
                    var subFeatureIntron = query('div.feature-render', featureNode);
                    // added to handle apollo annotation classes:  https://github.com/GMOD/Apollo/issues/1417
                    if (subFeatureIntron && subFeatureIntron.length == 1 && subFeatureIntron[0].className.indexOf('annot-apollo') < 0 && subFeatureIntron[0].className.indexOf('annot-render') < 0) {
                        var left = featureNode.style.left;
                        var width = featureNode.style.width;
                        var height = '100%';
                        var str = '';
                        str += "<svg class='jb-intron' viewBox='0 0 100 100' preserveAspectRatio='none' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' ";
                        str += "style='position:absolute;z-index: 15;";  // this must be here and not in CSS file
                        str += 'left: ' + left + ';width: ' + width + ';height: ' + height + "'>";
                        str += "<polyline points='0,50 100,50' style='fill:none;stroke:black;stroke-width:5' shape-rendering='optimizeQuality' />";
                        str += '</svg>';

                        // note: dojo.create("svg") does not render due to namespace issue between DOM and SVG
                        domConstruct.place(str, featureNode);
                    }
                } else if (subNodes.length >= 2) {
                    // identify directionality
                    var classAttr = dojo.attr(featureNode, 'class');
                    var direction = 1;
                    if (classAttr.indexOf('minus') > -1) {
                        direction = -1;
                    }
                    // extract some left & width -  more convient to access
                    for (var i = 0; i < subNodes.length; i++) {
                        subNodes[i].left = dojo.getStyle(subNodes[i], 'left');
                        subNodes[i].width = dojo.getStyle(subNodes[i], 'width');
                    }
                    // sort the subfeatures
                    if (subNodes.length >= 2) {
                        subNodes.sort(function (a, b) { return a.left - b.left; });
                    }

                    // insert introns between subfeature gaps
                    for (var i = 0; i < subNodes.length - 1; ++i) {
                        var gap = subNodes[i + 1].left - (subNodes[i].left + subNodes[i].width);
                        if (gap > 0.02) {
                            var subLeft = subNodes[i].left + subNodes[i].width;
                            var subWidth = subNodes[i + 1].left - (subNodes[i].left + subNodes[i].width);

                            var left = subLeft;
                            var width = subWidth;
                            var height = '100%';

                            // invert hat if reverse direction
                            var dir = '50,5';
                            if (direction == -1) dir = '50,95';

                            var str = '';
                            str += "<svg class='jb-intron' viewBox='0 0 100 100' preserveAspectRatio='none' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' ";
                            str += "style='position:absolute;z-index: 15;";  // this must be here and not in CSS file
                            str += 'left: ' + left + '%;width: ' + width + '%;height: ' + height + "'>";
                            str += "<polyline class='neat-intron' points='0,50 " + dir + " 100,50' shape-rendering='optimizeQuality' />";
                            str += '</svg>';

                            // note: dojo.create("svg") does not render due to namespace issue between DOM and SVG
                            domConstruct.place(str, featureNode);
                        }
                    }
                }
            }
        },
        /*
         * Paint neat features and subfeatures
         */
        paintNeatFeatures: function (featureNode) {
            // get the subfeature nodes (only immediate children)
            var subNodesX = query('> .subfeature', featureNode);
            var thisB = this;

            // filter nodes - eliminate nodes that are splice sites (for Apollo)
            var subNodes = [];
            for (var i = 0; i < subNodesX.length; i++) {
                var attr = dojo.attr(subNodesX[i], 'class');
                if (attr.indexOf('splice-site') === -1)                {
                    subNodes.push(subNodesX[i]);
                }
            }

            // if feature has subfeatures
            if (subNodes.length) {
                dojo.setStyle(featureNode, {
                    'background-color': 'transparent',
                    'border-width': '0px'
                });

                // paint subfeatures
                for (var i = 0; i < subNodes.length; i++) {
                    // if this is Apollo, we have another subfeature level to traverse
                    if (subNodes[i].childElementCount) {
                        // get the subfeature nodes (only immediate children)
                        var childNodes = query('> .subfeature', subNodes[i]);

                        for (var j = 0; j < childNodes.length; j++) {
                            thisB.paintSubNode(childNodes[j]);
                        }
                    }
                // handle the first level subfeature
                    else {
                        thisB.paintSubNode(subNodes[i]);
                    }
                }
            }
            // paint features that have no subfeatures
            else {
                // ignore if we have already processed node
                if (!dojo.hasClass(featureNode, 'neat-feature')) {
                    var classAttr = dojo.attr(featureNode, 'class');
                    var color = dojo.getStyle(featureNode, 'background-color');

                    // update the element with new styling
                    // if(dojo.hasClass(featureNode,'neat-linear-shading')){
                    if (this.config.gradient == 1) {
                        dojo.setStyle(featureNode, {
                            'background': 'linear-gradient(to bottom,  ' + color + ' 0%,#e5e5e5 50%,' + color + ' 100%)'
                        });
                    }
                    dojo.addClass(featureNode, 'neat-feature');
                }
            }
        },
        /*
         * apply neat modifications to feature sub-nodes
         */
        paintSubNode: function (subNode) {
            var classAttr = dojo.attr(subNode, 'class');
            var color = dojo.getStyle(subNode, 'background-color');

            // ignore if we have already processed node
            if (!dojo.hasClass(subNode, 'neat-subfeature')) {
                // restyle UTR
                if (classAttr.indexOf('UTR') > -1) {
                    dojo.setStyle(subNode, {
                        'top': '0px',
                        'border': '1px solid ' + color
                    });
                    // mark as neat subfeature
                    dojo.addClass(subNode, 'neat-UTR');
                }
                else {
                    // if(classAttr.indexOf('CDS') > -1 || classAttr.indexOf('exon') > -1) {
                    if (this.config.gradient == 1) {
                        dojo.setStyle(subNode, {
                            'top': '0px',
                            'background': 'linear-gradient(to bottom,  ' + color + ' 0%,#e5e5e5 50%,' + color + ' 100%)'
                        });
                    }
                }
                // mark that we have processed the node
                dojo.addClass(subNode, 'neat-subfeature');
            }
        }
    });
});
<commit_msg>fix double painting of introns<commit_after>define([
    'dojo/_base/declare',
    'dojo/_base/lang',
    'dojo/Deferred',
    'dojo/dom-construct',
    'dojo/query',
    'JBrowse/View/Track/HTMLFeatures'
],
function (
    declare,
    lang,
    Deferred,
    domConstruct,
    query,
    HTMLFeatures
) {
    return declare(HTMLFeatures, {
        renderFeature: function (feature, uniqueId, block, scale, labelScale, descriptionScale, containerStart, containerEnd) {
            var featureNode = this.inherited(arguments);
            var thisB = this;

            thisB.insertIntrons(featureNode);
            thisB.paintNeatFeatures(featureNode);
            return featureNode;
        },
        insertIntrons: function (featureNode) {
            // ignore if we have already processed this node
            if (!dojo.hasClass(featureNode, 'has-neat-introns')) {
                // get the subfeatures nodes (only immediate children)
                var subNodesX = query('> .subfeature', featureNode);

                // filter nodes - eliminate nodes that are splice sites (for Apollo)
                var subNodes = [];
                for (var i = 0; i < subNodesX.length; i++) {
                    var attr = dojo.attr(subNodesX[i], 'class');
                    if (attr.indexOf('splice-site') === -1  && attr.indexOf('stop_codon_read_through') === -1 && attr.indexOf('intron') === -1) {
                        subNodes.push(subNodesX[i]);
                    }
                }
                if (subNodes.length < 2) {
                    // apply introns to all feature tracks
                    var subFeatureIntron = query('div.feature-render', featureNode);
                    // added to handle apollo annotation classes:  https://github.com/GMOD/Apollo/issues/1417
                    if (subFeatureIntron && subFeatureIntron.length == 1 && subFeatureIntron[0].className.indexOf('annot-apollo') < 0 && subFeatureIntron[0].className.indexOf('annot-render') < 0) {
                        var left = featureNode.style.left;
                        var width = featureNode.style.width;
                        var height = '100%';
                        var str = '';
                        str += "<svg class='jb-intron' viewBox='0 0 100 100' preserveAspectRatio='none' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' ";
                        str += "style='position:absolute;z-index: 15;";  // this must be here and not in CSS file
                        str += 'left: ' + left + ';width: ' + width + ';height: ' + height + "'>";
                        str += "<polyline points='0,50 100,50' style='fill:none;stroke:black;stroke-width:5' shape-rendering='optimizeQuality' />";
                        str += '</svg>';

                        // note: dojo.create("svg") does not render due to namespace issue between DOM and SVG
                        domConstruct.place(str, featureNode);
                    }
                } else if (subNodes.length >= 2) {
                    // identify directionality
                    var classAttr = dojo.attr(featureNode, 'class');
                    var direction = 1;
                    if (classAttr.indexOf('minus') > -1) {
                        direction = -1;
                    }
                    // extract some left & width -  more convient to access
                    for (var i = 0; i < subNodes.length; i++) {
                        subNodes[i].left = dojo.getStyle(subNodes[i], 'left');
                        subNodes[i].width = dojo.getStyle(subNodes[i], 'width');
                    }
                    // sort the subfeatures
                    if (subNodes.length >= 2) {
                        subNodes.sort(function (a, b) { return a.left - b.left; });
                    }

                    // insert introns between subfeature gaps
                    for (var i = 0; i < subNodes.length - 1; ++i) {
                        var gap = subNodes[i + 1].left - (subNodes[i].left + subNodes[i].width);
                        if (gap > 0.02) {
                            var subLeft = subNodes[i].left + subNodes[i].width;
                            var subWidth = subNodes[i + 1].left - (subNodes[i].left + subNodes[i].width);

                            var left = subLeft;
                            var width = subWidth;
                            var height = '100%';

                            // invert hat if reverse direction
                            var dir = '50,5';
                            if (direction == -1) dir = '50,95';

                            var str = '';
                            str += "<svg class='jb-intron' viewBox='0 0 100 100' preserveAspectRatio='none' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' ";
                            str += "style='position:absolute;z-index: 15;";  // this must be here and not in CSS file
                            str += 'left: ' + left + '%;width: ' + width + '%;height: ' + height + "'>";
                            str += "<polyline class='neat-intron' points='0,50 " + dir + " 100,50' shape-rendering='optimizeQuality' />";
                            str += '</svg>';

                            // note: dojo.create("svg") does not render due to namespace issue between DOM and SVG
                            domConstruct.place(str, featureNode);
                        }
                    }
                }
            }
        },
        /*
         * Paint neat features and subfeatures
         */
        paintNeatFeatures: function (featureNode) {
            // get the subfeature nodes (only immediate children)
            var subNodesX = query('> .subfeature', featureNode);
            var thisB = this;

            // filter nodes - eliminate nodes that are splice sites (for Apollo)
            var subNodes = [];
            for (var i = 0; i < subNodesX.length; i++) {
                var attr = dojo.attr(subNodesX[i], 'class');
                if (attr.indexOf('splice-site') === -1)                {
                    subNodes.push(subNodesX[i]);
                }
            }

            // if feature has subfeatures
            if (subNodes.length) {
                dojo.setStyle(featureNode, {
                    'background': 'none',
                    'background-color': 'transparent',
                    'border-width': '0px'
                });

                // paint subfeatures
                for (var i = 0; i < subNodes.length; i++) {
                    // if this is Apollo, we have another subfeature level to traverse
                    if (subNodes[i].childElementCount) {
                        // get the subfeature nodes (only immediate children)
                        var childNodes = query('> .subfeature', subNodes[i]);

                        for (var j = 0; j < childNodes.length; j++) {
                            thisB.paintSubNode(childNodes[j]);
                        }
                    }
                // handle the first level subfeature
                    else {
                        thisB.paintSubNode(subNodes[i]);
                    }
                }
            }
            // paint features that have no subfeatures
            else {
                // ignore if we have already processed node
                if (!dojo.hasClass(featureNode, 'neat-feature')) {
                    var classAttr = dojo.attr(featureNode, 'class');
                    var color = dojo.getStyle(featureNode, 'background-color');

                    // update the element with new styling
                    // if(dojo.hasClass(featureNode,'neat-linear-shading')){
                    if (this.config.gradient == 1) {
                        dojo.setStyle(featureNode, {
                            'background': 'linear-gradient(to bottom,  ' + color + ' 0%,#e5e5e5 50%,' + color + ' 100%)'
                        });
                    }
                    dojo.addClass(featureNode, 'neat-feature');
                }
            }
        },
        /*
         * apply neat modifications to feature sub-nodes
         */
        paintSubNode: function (subNode) {
            var classAttr = dojo.attr(subNode, 'class');
            var color = dojo.getStyle(subNode, 'background-color');

            // ignore if we have already processed node
            if (!dojo.hasClass(subNode, 'neat-subfeature')) {
                // restyle UTR
                if (classAttr.indexOf('UTR') > -1) {
                    dojo.setStyle(subNode, {
                        'top': '0px',
                        'border': '1px solid ' + color
                    });
                    // mark as neat subfeature
                    dojo.addClass(subNode, 'neat-UTR');
                }
                else {
                    // if(classAttr.indexOf('CDS') > -1 || classAttr.indexOf('exon') > -1) {
                    if (this.config.gradient == 1) {
                        dojo.setStyle(subNode, {
                            'top': '0px',
                            'background': 'linear-gradient(to bottom,  ' + color + ' 0%,#e5e5e5 50%,' + color + ' 100%)'
                        });
                    }
                }
                // mark that we have processed the node
                dojo.addClass(subNode, 'neat-subfeature');
            }
        }
    });
});
<|endoftext|>


<commit_before>$(function(){
	$window = $('.window'),
	$bird = $('.bird'),
  fallTime = 1000,
  gapHeight = 120,
  gameState = 2,
  pipeId = 0;

	var int = setInterval(function(){
    if(gameState === 1){
      spawnPipe();
      movePipes();
    }
  }, 1300);

  var birdPosInterval = setInterval(function(){
    if(gameState === 1){
      birdPos();
    }
  }, 10);

	$window.mousedown(function(){
		birdFlap();
    if(gameState === 2){
      gameState = 1;
      deleteInterval();
    }
	});

	$(window).keydown(function(e){
		if(e.keyCode === 32){
			birdFlap();
      e.preventDefault();
      if(gameState === 2){
        gameState = 1;
        deleteInterval();
      }
		}
	});

  function deleteInterval(){
	  setTimeout(function(){
		  var int= setInterval(function(){
        if(gameState === 1){
          deletePipe();
        }
      }, 1300);
	  }, 2050);
  }

	function birdFlap(){
    if(gameState === 1 || gameState === 2){
      $bird.css('transform', 'rotate(-20deg)');
		  $bird.stop().animate({
			  bottom: '+=60px'
  		}, 200, function(){
        birdPos();
        $bird.css('transform', 'rotate(0deg)');
		  	$bird.stop().animate({
			    bottom: '-=60px'
  		  }, 300, 'linear', function(){
          birdPos();
          gravity();
        });
	  	});
    }
	}

	function gravity(){
    birdPercent = parseInt($bird.css('bottom')) / $window.height();
    totalFallTime = fallTime * birdPercent;
		$bird.stop().animate({
			bottom: '0'
		}, totalFallTime, 'linear');

    $bird.css('transform', 'rotate(90deg)');
	}

	function spawnPipe(){
    pipeId++;
		pipeTopHeight = Math.floor(Math.random() * ($window.height() - 250)) + 50;
		pipeBottomHeight = $window.height() - (pipeTopHeight + gapHeight);
		pipe = '

';
		$window.append(pipe);
	}

	function deletePipe(){
		$('.window .pipe').first().remove();
	}

	function movePipes(){
		$('.pipe').each(function(){
			$(this).animate({
				right: '+=160px'
			}, 1300, 'linear');
		});
	}

  function birdPos(){
    if(parseInt($bird.css('bottom')) === 0){
      gameEnd();
    }

    curPipe = $('.pipe:nth-of-type(4)');
    if(curPipe.length > 0){
      pipeTop = $('.pipe:nth-of-type(4) .topHalf');
      pipeBottom = $('.pipe:nth-of-type(4) .bottomHalf');
      if(($bird.offset().left + $bird.width()) >= curPipe.offset().left && $bird.offset().left <= (curPipe.offset().left + curPipe.width())){
        if($bird.offset().top < (curPipe.offset().top + pipeTop.height()) || ($bird.offset().top + $bird.height()) > ((curPipe.offset().top + pipeTop.height()) +   gapHeight)){
          gameEnd();
        }
      } else if($bird.offset().left >= (curPipe.offset().left + curPipe.width())){
        $('.score').text(curPipe.attr('pipe-id'));
      }
    }
  }

  function gameEnd(){
  	clearInterval(birdPosInterval);
  	$('.pipe').stop();
    gravity();
  	gameState = 0;
	  /*location.reload();*/
	 alert("Final Score" + $('score').text())
  }
})
<commit_msg>Update main.js<commit_after>$(function(){
	$window = $('.window'),
	$bird = $('.bird'),
  fallTime = 1000,
  gapHeight = 120,
  gameState = 2,
  pipeId = 0;

	var int = setInterval(function(){
    if(gameState === 1){
      spawnPipe();
      movePipes();
    }
  }, 1300);

  var birdPosInterval = setInterval(function(){
    if(gameState === 1){
      birdPos();
    }
  }, 10);

	$window.mousedown(function(){
		birdFlap();
    if(gameState === 2){
      gameState = 1;
      deleteInterval();
    }
	});

	$(window).keydown(function(e){
		if(e.keyCode === 32){
			birdFlap();
      e.preventDefault();
      if(gameState === 2){
        gameState = 1;
        deleteInterval();
      }
		}
	});

  function deleteInterval(){
	  setTimeout(function(){
		  var int= setInterval(function(){
        if(gameState === 1){
          deletePipe();
        }
      }, 1300);
	  }, 2050);
  }

	function birdFlap(){
    if(gameState === 1 || gameState === 2){
      $bird.css('transform', 'rotate(-20deg)');
		  $bird.stop().animate({
			  bottom: '+=60px'
  		}, 200, function(){
        birdPos();
        $bird.css('transform', 'rotate(0deg)');
		  	$bird.stop().animate({
			    bottom: '-=60px'
  		  }, 300, 'linear', function(){
          birdPos();
          gravity();
        });
	  	});
    }
	}

	function gravity(){
    birdPercent = parseInt($bird.css('bottom')) / $window.height();
    totalFallTime = fallTime * birdPercent;
		$bird.stop().animate({
			bottom: '0'
		}, totalFallTime, 'linear');

    $bird.css('transform', 'rotate(90deg)');
	}

	function spawnPipe(){
    pipeId++;
		pipeTopHeight = Math.floor(Math.random() * ($window.height() - 250)) + 50;
		pipeBottomHeight = $window.height() - (pipeTopHeight + gapHeight);
		pipe = '

';
		$window.append(pipe);
	}

	function deletePipe(){
		$('.window .pipe').first().remove();
	}

	function movePipes(){
		$('.pipe').each(function(){
			$(this).animate({
				right: '+=160px'
			}, 1300, 'linear');
		});
	}

  function birdPos(){
    if(parseInt($bird.css('bottom')) === 0){
      gameEnd();
    }

    curPipe = $('.pipe:nth-of-type(4)');
    if(curPipe.length > 0){
      pipeTop = $('.pipe:nth-of-type(4) .topHalf');
      pipeBottom = $('.pipe:nth-of-type(4) .bottomHalf');
      if(($bird.offset().left + $bird.width()) >= curPipe.offset().left && $bird.offset().left <= (curPipe.offset().left + curPipe.width())){
        if($bird.offset().top < (curPipe.offset().top + pipeTop.height()) || ($bird.offset().top + $bird.height()) > ((curPipe.offset().top + pipeTop.height()) +   gapHeight)){
          gameEnd();
        }
      } else if($bird.offset().left >= (curPipe.offset().left + curPipe.width())){
        $('.score').text(curPipe.attr('pipe-id'));
      }
    }
  }

  function gameEnd(){
  	clearInterval(birdPosInterval);
  	$('.pipe').stop();
    gravity();
  	gameState = 0;
	  /*location.reload();*/
	 alert("Final Score" + $('.score').text())
  }
})
<|endoftext|>