import React from 'react';
import PropTypes from 'prop-types';

import Control from './Control';

class TextInput extends Control {
  static propTypes = {
    className: PropTypes.string,
    id: PropTypes.string,
    name: PropTypes.string.isRequired,
    placeholder: PropTypes.string,
    style: PropTypes.string,
    type: PropTypes.oneOf(['text', 'email']),
  };

  static defaultProps = {
    type: 'text',
  };

  render() {
    const {placeholder, id, type, name, className, style} = this.props;

    return (
      <input
        placeholder={placeholder}
        id={id}
        className={className}
        style={style}
        type={type}
        name={name}
        value={this._getValue()}
        onChange={(e) => this._onChange(e.target.value)}
      />
    );
  }
}

export default TextInput;

import React from 'react';
import PropTypes from 'prop-types';

import {updateValue} from '../actions/controls';
import {connect} from '../store';

const TextInput = ({placeholder, id, type='text', name, className, style, value='', updateValue}) =>
  <input
    placeholder={placeholder}
    id={id}
    className={className}
    style={style}
    type={type}
    name={name}
    value={value}
    onChange={(e) => updateValue(name, e.target.value)}
  />;

TextInput.propTypes = {
  className: PropTypes.string,
  id: PropTypes.string,
  name: PropTypes.string.isRequired,
  placeholder: PropTypes.string,
  style: PropTypes.string,
  type: PropTypes.oneOf(['text', 'email']),
  value: PropTypes.string,
  updateValue: PropTypes.func.isRequired,
};

const mapStateToProps = ({controls}, props) => ({
  value: controls.get(props.name),
});

const mapDispatchToProps = {
  updateValue,
};

export default connect(mapStateToProps, mapDispatchToProps)(TextInput);

Simplify text input to functional component and to use connect


import fs from 'fs-extra';
import path from 'path';
import Snapcraft from 'electron-installer-snap/snapcraft';

import asyncOra from '../util/ora-handler';

/**
 * `forgeConfig.snapStore`:
 * * `release`: comma-separated list of channels to release to
 */
export default async ({ dir, artifacts, forgeConfig }) => {
  const snapArtifacts = artifacts.filter(artifact => artifact.endsWith('.snap'));

  if (snapArtifacts.length === 0) {
    throw 'No snap files to upload!';
  }

  const snapcraftCfgPath = path.join(dir, '.snapcraft', 'snapcraft.cfg');

  if (!await fs.pathExists(snapcraftCfgPath)) {
    throw 'Snapcraft config not found!';
  }

  await asyncOra('Pushing snap to the snap store', async () => {
    const snapcraft = new Snapcraft();
    await snapcraft.run(dir, 'push', forgeConfig.snapStore, snapArtifacts);
  });
};

import fs from 'fs-extra';
import path from 'path';
import Snapcraft from 'electron-installer-snap/snapcraft';

import asyncOra from '../util/ora-handler';

/**
 * `forgeConfig.snapStore`:
 * * `release`: comma-separated list of channels to release to
 */
export default async ({ dir, artifacts, forgeConfig }) => {
  const snapArtifacts = artifacts.filter(artifact => artifact.endsWith('.snap'));

  if (snapArtifacts.length === 0) {
    throw 'No snap files to upload. Please ensure that "snap" is listed in the "make_targets" in Forge config.';
  }

  const snapcraftCfgPath = path.join(dir, '.snapcraft', 'snapcraft.cfg');

  if (!await fs.pathExists(snapcraftCfgPath)) {
    throw `Snapcraft config not found, expected to be at "${snapcraftCfgPath}".`;
  }

  await asyncOra('Pushing snap to the snap store', async () => {
    const snapcraft = new Snapcraft();
    await snapcraft.run(dir, 'push', forgeConfig.snapStore, snapArtifacts);
  });
};

Update copy based on PR feedback


$(document).ready(function() {
  var matrixSettings = {
    id    : $('#matrix-id').text(),
    decay : 60000
  };

  $('h1, p, li, form input, table').hide(0, function() {
    $('h1').fadeIn(1000);
    $('p, li, form input, table').fadeIn(1500);
  });

  if ($('#matrix-id').length > 0) {
    var socket = new WebSocket(
      window.location.href.replace("http://", "ws://") + "/websocket"
    );

    socket.onmessage = function (e) {
      var data = $.parseJSON(e.data);

      if (data.chars) {
        $.each(data.chars, function(k, v) {
            var streamId  = 'stream-' + k.charCodeAt(0);

            if ($('#' + streamId).length === 0) {
              $('#matrix').append("

" + k.charAt(0) + "

");
            }

            $("
" + v.charAt(0) + "
").appendTo($('#' + streamId)).fadeTo(matrixSettings.decay, 0.3);
        });
      }
    };
  }
});

$(document).ready(function () {
  'use strict';

  var matrixSettings,
    socket;

  matrixSettings = {
    id    : $('#matrix-id').text(),
    decay : 60000
  };

  $('h1, p, li, form input, table').hide(0, function () {
    $('h1').fadeIn(1000);
    $('p, li, form input, table').fadeIn(1500);
  });

  if ($('#matrix-id').length > 0) {
    socket = new WebSocket(
      window.location.href.replace("http://", "ws://") + "/websocket"
    );

    socket.onmessage = function (e) {
      var data = $.parseJSON(e.data);

      if (data.chars) {
        $.each(data.chars, function (k, v) {
          var streamId,
            streamTailPrev,
            streamTail,
            streamItems;

          streamId  = 'stream-' + k.charCodeAt(0);

          if ($('#' + streamId).length === 0) {
            $('#matrix').append("

" + k.charAt(0) + "

");
          }

          streamTailPrev = $('#' + streamId + ' tbody tr.tail').removeClass('tail');

          streamTail = $("
" + v.charAt(0) + "
");

          if (streamTailPrev.length > 0) {
            streamTail.insertAfter(streamTailPrev);
          } else {
            streamTail.appendTo($('#' + streamId));
          }

          streamTail.fadeTo(matrixSettings.decay, 0.3);

          streamItems = $('#' + streamId + ' tbody tr');

          if (streamTail[0] === streamItems.last()[0] &&
              $('#' + streamId).height() > $(window).height()) {
            streamItems.first().replaceWith(streamTail);
          } else {
            streamTail.next().remove();
          }
        });
      }
    };
  }
});

Replace matrix rows when stream too tall.


define(function(require) {

  var BasePageView = require('./BasePageView'),
      TimeSelectorView = require('app/ui/views/TimeSelectorView'),
      BarChartView = require('app/ui/views/BarChartView'),
      revenueByEmployeeCollection = require('app/models/RevenueByEmployeeCollection'),
      SmallRevenueByCategoryView = require('app/ui/views/SmallRevenueByCategoryView'),
      timeRangeModel = require('app/models/TimeRangeModel');

  require('rdust!templates/employees');

  /**
   * Employee View
   * @class app.ui.pageviews.EmployeesView
   * @extends app.ui.pageviews.BasePageView
   */
  var EmployeesView = BasePageView.extend(function() {
    BasePageView.apply(this, arguments);
    this.mapChildView({
      '.time-selector': {
        TView: TimeSelectorView,
        model: timeRangeModel
      },
      '.bar-chart': {
        TView: BarChartView
      },
      '.revenue-by-category':{
        TView: SmallRevenueByCategoryView,
        model: revenueByEmployeeCollection
      }
    });
  }, {
    /**
     * The name of the template used by the view
     * @property {String} template
     * @default 'example'
     */
    template: 'templates/employees',
    /**
     * A class name added to the view container
     * @property {String} className
     * @default 'example'
     */
    className: 'employees'

  });

  return EmployeesView;

});

define(function(require) {

  var BasePageView = require('./BasePageView'),
      TimeSelectorView = require('app/ui/views/TimeSelectorView'),
      BarChartView = require('app/ui/views/BarChartView'),
      revenueByCategoryCollection = require('app/models/RevenueByCategoryCollection'),
      SmallRevenueByCategoryView = require('app/ui/views/SmallRevenueByCategoryView'),
      timeRangeModel = require('app/models/TimeRangeModel');

  require('rdust!templates/employees');

  /**
   * Employee View
   * @class app.ui.pageviews.EmployeesView
   * @extends app.ui.pageviews.BasePageView
   */
  var EmployeesView = BasePageView.extend(function() {
    BasePageView.apply(this, arguments);
    this.mapChildView({
      '.time-selector': {
        TView: TimeSelectorView,
        model: timeRangeModel
      },
      '.bar-chart': {
        TView: BarChartView
      },
      '.revenue-by-category': {
        TView: SmallRevenueByCategoryView,
        model: revenueByCategoryCollection
      }
    });
  }, {
    /**
     * The name of the template used by the view
     * @property {String} template
     * @default 'example'
     */
    template: 'templates/employees',
    /**
     * A class name added to the view container
     * @property {String} className
     * @default 'example'
     */
    className: 'employees'

  });

  return EmployeesView;

});

Fix data source for employees page


/**
 * Topic observer pattern base class
 */
export class TopicObserver {

    constructor() {
        this.observers = { __all__: [] };
    }

    observe(fn, topic) {

        // default is all
        topic = topic || '__all__';

        // create topic if needed
        if (!this.observers.hasOwnProperty(topic))
            this.observers[topic] = [];

        this.observers[topic].push(fn);
    }

    remove(fn, topic) {

        // default is all
        topic = topic || '__all__';

        this.observers = this.observers[topic].filter((subscriber) => subscriber !== fn);
    }

    broadcast(data, topic) {

        // default is all
        topic = topic || '__all__';

        // is that a particular topic?
        if (topic !== '__all__')
            this.observers[topic].forEach((subscriber) => subscriber(data));

        // broadcast for all
        this.observers['__all__'].forEach((subscriber) => subscriber(data));
    }

}

/**
 * Topic observer pattern base class
 */
export class TopicObserver {

    constructor() {
        this.observers = { __all__: [] };
    }

    observe(fn, topic) {

        // default is all
        topic = topic || '__all__';

        // create topic if needed
        if (!this.observers.hasOwnProperty(topic))
            this.observers[topic] = [];

        this.observers[topic].push(fn);
    }

    remove(fn, topic) {

        // default is all
        topic = topic || '__all__';

        this.observers[topic] = this.observers[topic].filter((subscriber) => subscriber !== fn);
    }

    broadcast(data, topic) {

        // default is all
        topic = topic || '__all__';

        // is that a particular topic?
        if (topic !== '__all__')
            this.observers[topic].forEach((subscriber) => subscriber(data));

        // broadcast for all
        this.observers['__all__'].forEach((subscriber) => subscriber(data));
    }

}

Put back changes in observer after unsuccessful commit.


const $ = jQuery = require("jquery")
const localforage = require("localforage")
const moment = require("moment")
const path = require("path")

const Audiosearch = require("audiosearch-client-node")
const audiosearch = new Audiosearch(
  process.env.CBUS_AUDIOSEARCH_APPLICATION_ID,
  process.env.CBUS_AUDIOSEARCH_SECRET
)

const $ = jQuery = require("jquery")
const localforage = require("localforage")
const moment = require("moment")
const path = require("path")

const Audiosearch = require("audiosearch-client-node")
const audiosearch = new Audiosearch(
  require("../../audiosearch_config.json").application_id,
  require("../../audiosearch_config.json").secret
)

Switch back to getting AS creds from file


const getManyComplete = require('./getManyComplete');

module.exports = (range, callback) => {
  // Get latest entries with their attachments and urls completed.
  //
  // Parameters:
  //   range
  //     skip
  //     limit
  //   callback
  //     function (err, entries)
  //
  return getManyComplete({
    deleted: false,
  }, {
    skip: range.skip,
    limit: range.limit,
    withLocations: true,
  }, callback);
};

const getManyComplete = require('./getManyComplete');

module.exports = (range, callback) => {
  // Get latest entries with their attachments and urls completed.
  //
  // Parameters:
  //   range
  //     skip
  //     limit
  //   callback
  //     function (err, entries)
  //
  return getManyComplete({
    deleted: false,
  }, {
    skip: range.skip,
    limit: range.limit,
    withLocations: true,
    sort: {
      activeAt: -1,
    },
  }, callback);
};

Sort latest entries by activeAt


import { Component } from 'substance'

export default class SelectInput extends Component {
  render($$) {
    const label = this.props.label
    const value = this.props.value
    const options = this.props.availableOptions
    const el = $$('div').addClass('sc-select-input')
    const selectEl = $$('select').addClass('se-select')
      .ref('input')
      .on('change', this._onChange)

    const defaultOpt = $$('option').attr({value: false})
      .append(this.getLabel('select-default-value'))

    if(!value) {
      defaultOpt.attr({selected: 'selected'})
    }

    selectEl.append(defaultOpt)

    options.forEach(opt => {
      const optEl = $$('option').attr({value: opt.id}).append(opt.text)
      if(opt.id === value) optEl.attr({selected: 'selected'})
      selectEl.append(optEl)
    })

    el.append(
      $$('div').addClass('se-label').append(label),
      selectEl
    )

    return el
  }

  _onChange() {
    const id = this.props.id
    if(id) {
      const value = this._getValue()
      this.send('input:change', id, value)
    }
  }

  _getValue() {
    const input = this.refs.input
    return input.value
  }
}

import { Component } from 'substance'

export default class SelectInput extends Component {
  render($$) {
    const label = this.props.label
    const value = this.props.value
    const options = this.props.availableOptions
    const el = $$('div').addClass('sc-select-input')
    const selectEl = $$('select').addClass('se-select')
      .ref('input')
      .on('change', this._onChange)

    const defaultOpt = $$('option').attr({value: false})
      .append(this.getLabel('select-default-value'))

    if(!value) {
      defaultOpt.attr({selected: 'selected'})
    }

    selectEl.append(defaultOpt)

    options.forEach(opt => {
      const optEl = $$('option').attr({value: opt.id}).append(opt.text)
      if(opt.id === value) optEl.attr({selected: 'selected'})
      selectEl.append(optEl)
    })

    el.append(
      $$('div').addClass('se-label').append(label),
      selectEl
    )

    return el
  }

  _onChange() {
    const name = this.props.name
    const value = this._getValue()
    this.send('set-value', name, value)
  }

  _getValue() {
    const input = this.refs.input
    return input.value
  }
}

Use a new updating strategy.


var config = { width: 300, height: 100, background: '#dedede', stroke: 'red' };

var serve = function(request, response) {
  var url_parts = request.url.split('/');

  var url = `https://atmospherejs.com/a/packages/findByNames?names=${url_parts[1]}`;
  var opt = {headers: {'Accept': 'application/json'}};
  HTTP.get(url, opt, function (err, res) {
    response.writeHead(200, { "Content-Type": "image/svg+xml" });
    response.write(`<svg xmlns="http://www.w3.org/2000/svg" height="${config.height}" width="${config.width}">
      <rect x="0" y="0" width="${config.width}" height="${config.height}" fill="${config.background}" stroke-width="4" stroke="${config.stroke}" />
      <text x="20" y="20" font-family="Verdana" font-size="30">${res.data[0].name}</text>
      </svg>`);
    response.end();
  })
};

WebApp.connectHandlers.use("/image", serve);

var config = { width: 300, height: 100, background: '#dedede', stroke: 'red' };

var serve = function(request, response) {
  var url_parts = request.url.split('/');

  var url = `https://atmospherejs.com/a/packages/findByNames?names=${url_parts[1]}`;
  var opt = {headers: {'Accept': 'application/json'}};
  HTTP.get(url, opt, function (err, res) {
    response.writeHead(200, { "Content-Type": "image/svg+xml" });
    response.write(`<svg xmlns="http://www.w3.org/2000/svg" height="${config.height}" width="${config.width}">
      <rect x="0" y="0" width="${config.width}" height="${config.height}" fill="${config.background}" stroke-width="4" stroke="${config.stroke}" />
      <text x="20" y="20" font-family="Verdana" font-size="30">${res.data[0].name}</text>
      </svg>`);
    response.end();
  });
};

WebApp.connectHandlers.use("/package", serve);

Use /package instead of /image in url


module.exports = {
	setUpListeners: function(socket) {
		socket.on('get:subscriptions', function() {
			socket.emit('got:subscriptions', ['OWSLA', 'Dirtybird']);
		});
	}
};

module.exports = {
	setUpListeners: setUpListeners
};

function setUpListeners(socket) {
	socket.on('get:subscriptions', function() {
		socket.emit('got:subscriptions', ['OWSLA', 'Dirtybird']);
	});
}

Move function out of module.exports to leave space


CRM.$(function ($) {
  displayActionEntity();
  $('#action_type').change(function () {
    displayActionEntity();
  });

});

/**
 * Set up the display of the action_entity_id field.
 * Hide if this action doesn't use it; if it does, set the label correctly,
 *
 * @returns {undefined}
 */
function displayActionEntity() {
  action_type = CRM.$('#action_type').val();
  params = {'action_type': action_type};
  entity = CRM.api3('FastActionLink', 'getaction', params)
    .done(function (result) {
      if (result.is_error) {
        CRM.$('#action_entity_id').parent().parent().hide();
      } else {
        entityLabel = ts("Select a") + " " + ts(result.entityLabel);
        CRM.$("label[for='action_entity_id']").html(entityLabel);
        CRM.$('#action_entity_id').val("");

        CRM.$('#action_entity_id').crmEntityRef({
          entity: result.entityName,
          placeholder: entityLabel,
          create: false,
          select: {'minimumInputLength': 0, 'width': 'resolve'},
          api: {params: result.apiParams},
        });
        CRM.$('#action_entity_id').parent().parent().show();
      }

    });

}

CRM.$(function ($) {
  displayActionEntity();
  $('#action_type').change(function () {
    CRM.$('#action_entity_id').val("");
    displayActionEntity();
  });

});

/**
 * Set up the display of the action_entity_id field.
 * Hide if this action doesn't use it; if it does, set the label correctly,
 *
 * @returns {undefined}
 */
function displayActionEntity() {
  action_type = CRM.$('#action_type').val();
  params = {'action_type': action_type};
  entity = CRM.api3('FastActionLink', 'getaction', params)
    .done(function (result) {
      if (result.is_error) {
        CRM.$('#action_entity_id').parent().parent().hide();
      } else {
        entityLabel = ts("Select a") + " " + ts(result.entityLabel);
        CRM.$("label[for='action_entity_id']").html(entityLabel);

        CRM.$('#action_entity_id').crmEntityRef({
          entity: result.entityName,
          placeholder: entityLabel,
          create: false,
          select: {'minimumInputLength': 0, 'width': 'resolve'},
          api: {params: result.apiParams},
        });
        CRM.$('#action_entity_id').parent().parent().show();
      }

    });

}

Fix display of the action when loaded a saved FAL


const log = require('@cardstack/logger')('cardstack/email');
const nodemailer = require('nodemailer');

module.exports = class EmailMessenger {
  static create(params) {
    return new this(params);
  }
  constructor(params) {
    this.params = params;
    ['smtpPasswordEnvVar', 'smtpHost', 'smtpUser', 'defaultFrom'].forEach( prop => {
      if (!params[prop]) {
        throw new Error(`Could not find ${prop} in message sink params`);
      }
    });
    this.transporter = nodemailer.createTransport({
      host: params.smtpHost,
      auth: {
        user: params.smtpUser,
        pass: process.env[params.smtpPasswordEnvVar]
      }
    }, {
      from: params.defaultFrom
    });
  }
  async send(message) {
    log.info(`Sending mail to ${message.to}: ${message.subject}`);
    return this.transporter.sendMail(message);
  }
};

const log = require('@cardstack/logger')('cardstack/email');
const nodemailer = require('nodemailer');

module.exports = class EmailMessenger {
  static create(params) {
    return new this(params);
  }
  constructor(params) {
    this.params = params;

    if(params.useSes) {
      // If SES is required, you must add aws-sdk to the consuming project
      const aws = require("aws-sdk");  // eslint-disable-line node/no-missing-require
      if (!params.defaultFrom) {
        throw new Error(`Could not find defaultFrom in message sink params`);
      }

      this.transporter = nodemailer.createTransport({
        SES: new aws.SES({ apiVersion: '2010-12-01' })
      }, {
        from: params.defaultFrom
      });

    } else {
      ['smtpPasswordEnvVar', 'smtpHost', 'smtpUser', 'defaultFrom'].forEach( prop => {
        if (!params[prop]) {
          throw new Error(`Could not find ${prop} in message sink params`);
        }
      });
      this.transporter = nodemailer.createTransport({
        host: params.smtpHost,
        auth: {
          user: params.smtpUser,
          pass: process.env[params.smtpPasswordEnvVar]
        }
      }, {
        from: params.defaultFrom
      });
    }

  }
  async send(message) {
    log.info(`Sending mail to ${message.to}: ${message.subject}`);
    return this.transporter.sendMail(message);
  }
};

Allow email sink to use SES configured automatically from env vars


var ObjectEditor = {
    Data: {
        Units: require('../WEdata/data/Units.json'),
        Items: require('../WEdata/data/Items.json'),
        Destructables: require('../WEdata/data/Destructables.json')
    },
    Fields: {
        Units: require('../WEdata/fields/UnitMetaData.json'),
        Items: require('../WEdata/fields/ItemMetaData.json'),
        Destructables: require('../WEdata/fields/DestructableMetaData.json')
    },

    Find: {
        UnitFieldIdByLongName: function(longName) {
            // e.g. bountydice -> ubdi
            // TODO: time this function -- it may take too long for lookups

            for(var fieldKey in ObjectEditor.Fields.Units) {
                if( ObjectEditor.Fields.Units[fieldKey] &&
                    ObjectEditor.Fields.Units[fieldKey].field.toLowerCase() === longName.toLowerCase()) {
                    return fieldKey;
                }
            }

            // console.error('Unable to lookup unit field by long name:', longName);
            return null; // Unable to find by long name
        },
        FieldLongNameById: function(objEditorType, id) {
            // e.g. ubdi -> bountydice
            var objEditorFieldName = objEditorType[0].toUpperCase() + objEditorType.substr(1) + 's';
            return ObjectEditor.Fields[objEditorFieldName][id].field;
        }
    }
}

module.exports = ObjectEditor;

var ObjectEditor = {
    Data: {
        Units: require('../WEdata/data/Units.json'),
        Items: require('../WEdata/data/Items.json'),
        Destructables: require('../WEdata/data/Destructables.json')
    },
    Fields: {
        Units: require('../WEdata/fields/UnitMetaData.json'),
        Items: require('../WEdata/fields/ItemMetaData.json'),
        Destructables: require('../WEdata/fields/DestructableMetaData.json')
    },

    Find: {
        UnitFieldIdByLongName: function(longName) {
            // e.g. bountydice -> ubdi
            // TODO: time this function -- it may take too long for lookups

            for(var fieldKey in ObjectEditor.Fields.Units) {
                if( ObjectEditor.Fields.Units[fieldKey] &&
                    ObjectEditor.Fields.Units[fieldKey].field.toLowerCase() === longName.toLowerCase()) {
                    return fieldKey;
                }
            }

            // console.error('Unable to lookup unit field by long name:', longName);
            return null; // Unable to find by long name
        },
        FieldLongNameById: function(objEditorType, id) {
            // e.g. ubdi -> bountydice
            var objEditorFieldName = objEditorType[0].toUpperCase() + objEditorType.substr(1) + 's';
            return ObjectEditor.Fields[objEditorFieldName][id].field;
        }
    }
}

Object.keys(ObjectEditor.Data).forEach((data) => {
    console.log(data, Object.keys(ObjectEditor.Data[data]).length, Object.keys(ObjectEditor.Fields[data]).length);
})

module.exports = ObjectEditor;

Add debug code to list how many object entities there are for each type, along with number of fields


const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
  service: 'SendGrid',
  auth: {
    user: process.env.SENDGRID_USER,
    pass: process.env.SENDGRID_PASSWORD
  }
});

/**
 * GET /contact
 * Contact form page.
 */
exports.getContact = (req, res) => {
  res.render('contact', {
    title: 'Contact'
  });
};

/**
 * POST /contact
 * Send a contact form via Nodemailer.
 */
exports.postContact = (req, res) => {
  req.assert('name', 'Name cannot be blank').notEmpty();
  req.assert('email', 'Email is not valid').isEmail();
  req.assert('message', 'Message cannot be blank').notEmpty();

  const errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/contact');
  }

  const mailOptions = {
    to: 'your@email.com',
    from: `${req.body.name} <${req.body.email}>`,
    subject: 'Contact Form | Hackathon Starter',
    text: req.body.message
  };

  transporter.sendMail(mailOptions, (err) => {
    if (err) {
      req.flash('errors', { msg: err.message });
      return res.redirect('/contact');
    }
    req.flash('success', { msg: 'Email has been sent successfully!' });
    res.redirect('/contact');
  });
};

const nodemailer = require('nodemailer');
const Contact = require('../models/Contact');
const transporter = nodemailer.createTransport({
  service: 'SendGrid',
  auth: {
    user: process.env.SENDGRID_USER,
    pass: process.env.SENDGRID_PASSWORD
  }
});

/**
 * GET /contact
 * Contact form page.
 */
exports.getContact = (req, res) => {
  res.render('contact', {
    title: 'Contact'
  });
};

/**
 * POST /contact
 * Send a contact form via Nodemailer.
 */
exports.postContact = (req, res) => {

const cont = new Contact({
    mtype: req.body.missiontype,
    mdesc: req.body.missiondesc,
    mdatetime: req.body.missiondate,
    mbudget: req.body.budget
  });

cont.save((err) => {
      if (err) { return next(err); }
      req.flash('success', { msg: 'You have successfully added mission' });
        return res.redirect('/');
    });

};

Update on the mission page in controller folder


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

require.config({
  shim: {
    underscore: {
      exports: '_'
    },
    backbone: {
      deps: [
        'underscore',
        'jquery'
      ],
      exports: 'Backbone'
    }
  },
  paths: {
    jquery: '../bower_components/jquery/dist/jquery',
    backbone: '../bower_components/backbone/backbone',
    underscore: '../bower_components/underscore/underscore',
    text: '../bower_components/requirejs-text/text',
    mustache: '../bower_components/mustache/mustache',
    stache: '../bower_components/requirejs-mustache/stache'
  }
});

require([
  'jquery',
  'backbone',
  'router',
  'collections/devices'
], function ($, Backbone, Router, Devices) {
  // Bring on the globals
  window.devices = new Devices();

  // Fetch devices from the server
  window.devices.fetch().then(function() {
    window.currentDevice = window.devices.at(0);

    // Now that we have devices we can start
    Backbone.history.start();
  });

  // Temporary logout handing
  $(document).on('click', 'a.signout', function(event) {
    event.preventDefault();

    document.cookie = 'user=;expires=Thu, 01 Jan 1970 00:00:00 GMT';
    document.location = '/';
  });
});

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

require.config({
  shim: {
    underscore: {
      exports: '_'
    },
    backbone: {
      deps: [
        'underscore',
        'jquery'
      ],
      exports: 'Backbone'
    }
  },
  paths: {
    jquery: '../bower_components/jquery/dist/jquery',
    backbone: '../bower_components/backbone/backbone',
    underscore: '../bower_components/underscore/underscore',
    text: '../bower_components/requirejs-text/text',
    mustache: '../bower_components/mustache/mustache',
    stache: '../bower_components/requirejs-mustache/stache'
  }
});

require([
  'jquery',
  'backbone',
  'router',
  'collections/devices'
], function ($, Backbone, Router, Devices) {
  // Bring on the globals
  window.devices = new Devices();

  // Fetch devices from the server
  window.devices.fetch().then(function() {
    window.currentDevice = window.devices.last();

    // Now that we have devices we can start
    Backbone.history.start();
  });

  // Temporary logout handing
  $(document).on('click', 'a.signout', function(event) {
    event.preventDefault();

    document.cookie = 'user=;expires=Thu, 01 Jan 1970 00:00:00 GMT';
    document.location = '/';
  });
});

Use the last device instead of the first device until we have multiple device support.


var fs = require('fs');
var expect = require('chai').expect;

var updateLicense = require('../lib/update-license');

describe("update-license", function() {
  it("updates 2015 to 2015-2016", function() {
    var yearInfo = 'Copyright (c) 2015 Sung Won Cho';
    var result = updateLicense(yearInfo);
    expect(result).to.equal('Copyright (c) 2015-2016 Sung Won Cho');
  });

  it("updates 2012-2015 to 2012-2016", function() {
    var yearInfo = 'Copyright (c) 2012-2015 Sung Won Cho';
    var result = updateLicense(yearInfo);
    expect(result).to.equal('Copyright (c) 2012-2016 Sung Won Cho');
  });
});

var fs = require('fs');
var expect = require('chai').expect;

var updateLicense = require('../lib/update-license');

describe("update-license", function() {
  it("updates 2015 to 2015-2016", function(done) {
    var yearInfo = 'Copyright (c) 2015 Sung Won Cho';
    var result = updateLicense(yearInfo, function (err, updatedLicenseContent) {
      expect(updatedLicenseContent).to.equal('Copyright (c) 2015-2016 Sung Won Cho');
      done();
    });
  });

  it("updates 2012-2015 to 2012-2016", function(done) {
    var yearInfo = 'Copyright (c) 2012-2015 Sung Won Cho';
    var result = updateLicense(yearInfo, function (err, updatedLicenseContent) {
      expect(updatedLicenseContent).to.equal('Copyright (c) 2012-2016 Sung Won Cho');
      done();
    });
  });
});

Fix test using new asynchronous API


/*global browser:false, element:false, by:false */
'use strict';

var Synonyms = require('./synonyms');

var Taxonomy = function(id){
    this.id = id;
};

Taxonomy.prototype.getSynonyms = function(conceptName) {
    var synonyms = new Synonyms(this.id, conceptName);
    return synonyms;
};

Taxonomy.prototype.get = function(){
    return browser.get('/' + this.id);
};

Taxonomy.prototype.createConcept = function(conceptName){
    var input = element(by.model('conceptName'));
    input.clear();
    input.sendKeys(conceptName);
    var createConceptBtn = element.all(by.repeater('match in matches track by $index')).last();
    createConceptBtn.click();
    this.conceptName = element(by.id('concept')).element(by.binding('concept.Name')).getText();
};

Taxonomy.prototype.createElement = function(conceptName, parent, offset){
    offset = offset ? offset : 0;
    browser.get('/' + this.id + '/concept/' + conceptName +'?action=addElement&parent=' + parent + '&offset=' + offset);
    browser.waitForAngular();
    this.get();
};

module.exports = Taxonomy;

/*global browser:false, element:false, by:false */
'use strict';

var Synonyms = require('./synonyms');

var Taxonomy = function(id){
    this.id = id;
};

Taxonomy.prototype.getSynonyms = function(conceptName) {
    var synonyms = new Synonyms(this.id, conceptName);
    return synonyms;
};

Taxonomy.prototype.get = function(){
    return browser.get('/' + this.id);
};

Taxonomy.prototype.createConcept = function(conceptName){
    var input = element(by.model('conceptName'));
    input.clear();
    input.sendKeys(conceptName);
    var createConceptBtn = element.all(by.repeater('match in matches track by $index')).last();
    createConceptBtn.click();
    this.conceptName = element(by.id('concept')).element(by.binding('concept.Name')).getText();
};

Taxonomy.prototype.createElement = function(conceptName, parent, offset){
    offset = offset ? offset : 0;
    browser.get('/' + this.id + '/concept/' + conceptName +'?action=addElement&parent=' + parent + '&offset=' + offset);
    this.get();
};

module.exports = Taxonomy;

Remove useless promise in test suite


(function($) {
  var _addToggleLink = function(contentSelector) {
    var section = $(this);
    var content = $(contentSelector, section);

    content.hide();
    var toggleLink = $.a("show", {"class": "toggle", href: "#"});
    $("h1", section).append(toggleLink);
    toggleLink.click(function() {
      var currentLinkText = $(this).text();
      if (currentLinkText == "show") {
        $(this).text("hide");
      } else {
        $(this).text("show");
      }
      content.toggle();
    });
  };

  $.fn.extend({
    addToggleLink: _addToggleLink
  });
})(jQuery);

(function($) {
  var _addToggleLink = function(contentSelector) {
    var section = $(this);
    var content = $(contentSelector, section);

    content.hide();
    var toggleLink = $.a("show", {"class": "toggle", href: "#"});
    $("h1", section).append(toggleLink);
    toggleLink.click(function() {
      var currentLinkText = $(this).text();
      if (currentLinkText == "show") {
        $(this).text("hide");
      } else {
        $(this).text("show");
      }
      content.toggle();
      return false;
    });
  };

  $.fn.extend({
    addToggleLink: _addToggleLink
  });
})(jQuery);

Stop jumping to the top of the page.


define(function (require, exports, module) {
  'use strict';

  function CodeWriter(indentString) {
    this.lines = [];
    this.indentString = (indentString ? indentString : '  ');
    this.indentations = [];
  }

  CodeWriter.prototype.indent = function () {
    this.indentations.push(this.indentString);
  };

  CodeWriter.prototype.outdent = function () {
    this.indentations.splice(this.indentations.length - 1, 1);
  };

  CodeWriter.prototype.writeLine = function (line) {
    if (line) {
      this.lines.push(this.indentations.join('') + line);
    } else {
      this.lines.push('');
    }
  };

  CodeWriter.prototype.getData = function () {
    return this.lines.join('\n');
  };

  CodeWriter.prototype.fileName = function (className) {
    return className.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();
  };

  exports.CodeWriter = CodeWriter;
});

define(function (require, exports, module) {
  'use strict';

  function CodeWriter(indentString) {
    this.lines = [];
    this.indentString = (indentString ? indentString : '  ');
    this.indentations = [];
  }

  CodeWriter.prototype.indent = function () {
    this.indentations.push(this.indentString);
  };

  CodeWriter.prototype.outdent = function () {
    this.indentations.splice(this.indentations.length - 1, 1);
  };

  CodeWriter.prototype.writeLine = function (line) {
    if (line) {
      this.lines.push(this.indentations.join('') + line);
    } else {
      this.lines.push('');
    }
  };

  CodeWriter.prototype.getData = function () {
    return this.lines.join('\n');
  };

  CodeWriter.prototype.fileName = function (className) {
    return className.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();
  };

  CodeWriter.prototype.toCamelCase = function (className) {
    return className.replace(/(\b|_)\w/g, function (match) {
      return match.replace(/_/, '').toUpperCase();
    });
  };

  exports.CodeWriter = CodeWriter;
});

Add function to change string to upper camel case


/* Turkish translation for the jQuery Timepicker Addon */
/* Written by Fehmi Can Saglam */
(function($) {
	$.timepicker.regional['tr'] = {
		timeOnlyTitle: 'Zaman Seçiniz',
		timeText: 'Zaman',
		hourText: 'Saat',
		minuteText: 'Dakika',
		secondText: 'Saniye',
		millisecText: 'Milisaniyelik',
		timezoneText: 'Zaman Dilimi',
		currentText: 'Şu an',
		closeText: 'Tamam',
		timeFormat: 'hh:mm',
		amNames: ['AM', 'A'],
		pmNames: ['PM', 'P'],
		ampm: false
	};
	$.timepicker.setDefaults($.timepicker.regional['tr']);
})(jQuery);

/* Turkish translation for the jQuery Timepicker Addon */
/* Written by Fehmi Can Saglam, Edited by Goktug Ozturk */
(function($) {
	$.timepicker.regional['tr'] = {
		timeOnlyTitle: 'Zaman Seçiniz',
		timeText: 'Zaman',
		hourText: 'Saat',
		minuteText: 'Dakika',
		secondText: 'Saniye',
		millisecText: 'Milisaniye',
		timezoneText: 'Zaman Dilimi',
		currentText: 'Şu an',
		closeText: 'Tamam',
		timeFormat: 'hh:mm',
		amNames: ['ÖÖ', 'Ö'],
		pmNames: ['ÖS', 'S'],
		ampm: false
	};
	$.timepicker.setDefaults($.timepicker.regional['tr']);
})(jQuery);

Update Turkish translation by Goktug Ozturk


import React, { Fragment, PureComponent } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import theme from './theme.css';

class Link extends PureComponent {
  render() {
    const { children, className, icon, iconPlacement, element, inherit, ...others } = this.props;

    const classNames = cx(
      theme['link'],
      {
        [theme['inherit']]: inherit,
      },
      className,
    );

    const ChildrenWrapper = icon ? 'span' : Fragment;
    const Element = element;

    return (
      <Element className={classNames} data-teamleader-ui="link" {...others}>
        {icon && iconPlacement === 'left' && icon}
        <ChildrenWrapper>{children}</ChildrenWrapper>
        {icon && iconPlacement === 'right' && icon}
      </Element>
    );
  }
}

Link.propTypes = {
  children: PropTypes.node.isRequired,
  className: PropTypes.string,
  /** The icon displayed inside the button. */
  icon: PropTypes.element,
  /** The position of the icon inside the button. */
  iconPlacement: PropTypes.oneOf(['left', 'right']),
  inherit: PropTypes.bool,
  element: PropTypes.element,
};

Link.defaultProps = {
  className: '',
  element: 'a',
  inherit: true,
};

export default Link;

import React, { Fragment, PureComponent } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import theme from './theme.css';

class Link extends PureComponent {
  render() {
    const { children, className, icon, iconPlacement, element, inherit, ...others } = this.props;

    const classNames = cx(
      theme['link'],
      {
        [theme['inherit']]: inherit,
      },
      className,
    );

    const ChildrenWrapper = icon ? 'span' : Fragment;
    const Element = element;

    return (
      <Element className={classNames} data-teamleader-ui="link" {...others}>
        {icon && iconPlacement === 'left' && icon}
        <ChildrenWrapper>{children}</ChildrenWrapper>
        {icon && iconPlacement === 'right' && icon}
      </Element>
    );
  }
}

Link.propTypes = {
  children: PropTypes.node.isRequired,
  className: PropTypes.string,
  /** The icon displayed inside the button. */
  icon: PropTypes.element,
  /** The position of the icon inside the button. */
  iconPlacement: PropTypes.oneOf(['left', 'right']),
  inherit: PropTypes.bool,
  element: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),
};

Link.defaultProps = {
  className: '',
  element: 'a',
  inherit: true,
};

export default Link;

Add string as a possible prop type


Template.foundbill.created = function(){
  Meteor.subscribe("allbills");
  Session.set('message', '');
};

Template.foundbill.events({
  "submit form": function(e){
     e.preventDefault();

     var serial = e.target.serialnumber.value;
     serial = serial.toLowerCase();
     var zip = e.target.zip.value;
     var have = e.target.have.checked;
     var note = e.target.note.value;
     var email = Meteor.user().emails[0].address;

     var thisbill = bills.findOne({'serial': serial});

     // check to see if this bill exists
     if (thisbill){
       var billId = thisbill._id;

       bills.update({_id:billId},
       {$push: {
         "history": {
           timestamp: new Date(),
           zip: zip,
           recordedby: Meteor.userId(),
           note: note
         }
       }
      });

      e.target.serialnumber.value = '';
      e.target.zip.value = '';
      e.target.note.value = '';
      e.target.have.checked = false;

      Session.set('message', '
Thanks for registering this bill!
')
    }else{
      Session.set('message', '
Couldn\'t find this bill in our records. Register it as new here!
');
    }

  }
});

Template.foundbill.helpers({
  alertbox: function(){
    return Session.get('message');
  }
});

Template.foundbill.created = function(){
  Meteor.subscribe("allbills");
  Session.set('message', '');
};

Template.foundbill.events({
  "submit form": function(e){
     e.preventDefault();

     var serial = e.target.serialnumber.value;
     //serial = serial.toLowerCase();
     var zip = e.target.zip.value;
     var have = e.target.have.checked;
     var note = e.target.note.value;
     var email = Meteor.user().emails[0].address;

     var thisbill = bills.findOne({'serial': serial.toLowerCase()});

     // check to see if this bill exists
     if (thisbill){
       var billId = thisbill._id;

       bills.update({_id:billId},
       {$push: {
         "history": {
           timestamp: new Date(),
           zip: zip,
           recordedby: Meteor.userId(),
           note: note
         }
       }
      });

      e.target.serialnumber.value = '';
      e.target.zip.value = '';
      e.target.note.value = '';
      e.target.have.checked = false;

      Session.set('message', '
Thanks for registering this bill!
')
    }else{
      Session.set('message', '
Couldn\'t find this bill in our records. Register it as new here!
');
    }

  }
});

Template.foundbill.helpers({
  alertbox: function(){
    return Session.get('message');
  }
});

Change location of lower case conversion to fix issue.


'use strict'

const semver = require('semver')

exports.stable = (versions) => {
  const matched = versions.find((version) => typeof version.lts === 'undefined').version
  return semver.gte(matched, '5.0.0') ? matched : undefined
}

exports.lts = (versions) => versions.find((version) => typeof version.lts !== 'undefined').version

'use strict'

const semver = require('semver')

exports.stable = (releases) => {
  const match = releases.find((release) => !release.lts && semver.gte(release.version, '5.0.0'))
  return match && match.version
}

exports.lts = (releases) => releases.find((release) => release.lts).version

Fix bug calling v5 LTS.


'use strict';

const SelectListView = require('atom-space-pen-views').SelectListView;

const Deferred = require('./Deferred');

// TODO should this use a context menu instead of an overlay at the top of the
// window?
class AskForSelectionView extends SelectListView {
  /**
   * @param {Function} callback
   */
  initialize() {
    super.initialize();
    this.addClass('overlay from-top');
    this.storeFocusedElement();
    this.panel = atom.workspace.addModalPanel({ item: this });
    this.deferred = new Deferred();
    this.show();
  }

  viewForItem(item) {
    return `<li>${item.displayName}</li>`;
  }

  confirmed(item) {
    this.deferred.resolve(item);
    this.hide();
  }

  show() {
    if (this.panel) {
      this.panel.show();
      this.focusFilterEditor();
    }
  }

  hide() {
    if (this.panel) {
      this.panel.hide();
    }
  }

  cancelled() {
    this.deferred.reject();
    this.hide();
  }

  destroy() {
    this.cancel();
    this.panel.destroy();
  }
}

module.exports = AskForSelectionView;

'use strict';

const SelectListView = require('atom-space-pen-views').SelectListView;

const Deferred = require('./Deferred');

// TODO should this use a context menu instead of an overlay at the top of the
// window?
class AskForSelectionView extends SelectListView {
  /**
   * @param {Function} callback
   */
  initialize() {
    super.initialize();
    this.addClass('overlay from-top');
    this.storeFocusedElement();
    this.panel = atom.workspace.addModalPanel({ item: this });
    this.deferred = new Deferred();
    this.show();
  }

  getFilterKey() {
    return 'displayName';
  }

  viewForItem(item) {
    return `<li>${item.displayName}</li>`;
  }

  confirmed(item) {
    this.deferred.resolve(item);
    this.hide();
  }

  show() {
    if (this.panel) {
      this.panel.show();
      this.focusFilterEditor();
    }
  }

  hide() {
    if (this.panel) {
      this.panel.hide();
    }
  }

  cancelled() {
    this.deferred.reject();
    this.hide();
  }

  destroy() {
    this.cancel();
    this.panel.destroy();
  }
}

module.exports = AskForSelectionView;

Fix filtering for item objects


/*
 * This is the default (production) config for the discovery pane app.
 */

const amoCDN = 'https://addons.cdn.mozilla.net';
const staticHost = 'https://addons-discovery.cdn.mozilla.net';

module.exports = {
  // The keys listed here will be exposed on the client.
  // Since by definition client-side code is public these config keys
  // must not contain sensitive data.
  clientConfigKeys: [
    'appName',
    'amoCDN',
    'apiHost',
    'apiPath',
    'cookieName',
    'cookieMaxAge',
    'cookieSecure',
    'enableClientConsole',
    'defaultLang',
    'isDeployed',
    'isDevelopment',
    'langs',
    'langMap',
    'rtlLangs',
    'trackingEnabled',
    'trackingId',
    'useUiTour',
  ],

  staticHost,

  CSP: {
    directives: {
      // Script is limited to the discovery specific CDN.
      scriptSrc: [
        staticHost,
        'https://www.google-analytics.com/analytics.js',
      ],
      styleSrc: [staticHost],
      imgSrc: [
        "'self'",
        'data:',
        amoCDN,
        staticHost,
        'https://www.google-analytics.com',
      ],
      mediaSrc: [staticHost],
    },
  },
  trackingEnabled: true,
  trackingId: 'UA-36116321-7',

  enablePostCssLoader: false,

  // If this is false we'll use an in-page
  // stand-in for the ui-tour.
  useUiTour: false,

  po2jsonFuzzyOutput: true,
};

/*
 * This is the default (production) config for the discovery pane app.
 */

const amoCDN = 'https://addons.cdn.mozilla.net';
const staticHost = 'https://addons-discovery.cdn.mozilla.net';

module.exports = {
  // The keys listed here will be exposed on the client.
  // Since by definition client-side code is public these config keys
  // must not contain sensitive data.
  clientConfigKeys: [
    'appName',
    'amoCDN',
    'apiHost',
    'apiPath',
    'cookieName',
    'cookieMaxAge',
    'cookieSecure',
    'enableClientConsole',
    'defaultLang',
    'isDeployed',
    'isDevelopment',
    'langs',
    'langMap',
    'rtlLangs',
    'trackingEnabled',
    'trackingId',
    'useUiTour',
  ],

  staticHost,

  CSP: {
    directives: {
      // Script is limited to the discovery specific CDN.
      scriptSrc: [
        staticHost,
        'https://www.google-analytics.com/analytics.js',
      ],
      styleSrc: [staticHost],
      imgSrc: [
        "'self'",
        'data:',
        amoCDN,
        staticHost,
        'https://www.google-analytics.com',
      ],
      mediaSrc: [staticHost],
    },
  },
  trackingEnabled: true,
  trackingId: 'UA-36116321-7',

  enablePostCssLoader: false,

  // If this is false we'll use an in-page
  // stand-in for the ui-tour.
  useUiTour: false,

  po2jsonFuzzyOutput: false,
};

Disable fuzzy output in po2json for disco


var map;
function initMap() {
    map = new google.maps.Map(document.getElementById("map"), {
        center: { lat: 32.819595, lng: -96.945419},
        zoom: 10
    });
}

let map;

function initMap() {
    map = new google.maps.Map(document.getElementById("map"), {
        center: { lat: 32.819595, lng: -96.945419},
        zoom: 10
    });
}

Change map from var to let


module.exports = [
    {
        test: /\.ts(x?)$/,
        loader: 'ts-loader'
    }, {
        test: /\.css$/,
        loader: 'style-loader!css-loader'
    }, {
        test: /\.scss$/,
        loader: 'style-loader!css-loader!sass-loader'
    }, {
        test: /\.html$/,
        exclude: /node_modules/,
        loader: 'raw-loader'
    }, {
        test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'url-loader?limit=10000&mimetype=application/font-woff'
    }, {
        test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader'
    }, {
        test: /\.jpg$/,
        exclude: /node_modules/,
        loader: 'file-loader'
    }, {
        test: /\.png$/,
        exclude: /node_modules/,
        loader: 'url-loader'
    }
];

module.exports = [
    {
        test: /\.ts(x?)$/,
        loader: "ts-loader"
    }, {
        test: /\.css$/,
        loader: "style-loader!css-loader"
    }, {
        test: /\.scss$/,
        loader: "style-loader!css-loader!sass-loader"
    }, {
        test: /\.html$/,
        exclude: /node_modules/,
        loader: "raw-loader"
    }, {
        test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: "url-loader?limit=10000&mimetype=application/font-woff"
    }, {
        test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: "file-loader"
    }, {
        test: /\.jpg$/,
        exclude: /node_modules/,
        loader: "file-loader"
    }, {
        test: /\.png$/,
        exclude: /node_modules/,
        loader: "url-loader"
    }
];

Replace single quotes -> double quotes


import Vue from 'vue';
import Router from 'vue-router';
import Home from '@/pages/Home';
import Blog from '@/pages/Blog';
import Page from '@/pages/Page';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
    },
    {
      path: '/blog',
      name: 'Blog',
      component: Blog,
    },
    {
      path: '/blog/:slug',
      name: 'Page',
      component: Page,
    },
  ],
  scrollBehavior() {
    return { x: 0, y: 0 };
  },
});

import Vue from 'vue';
import Router from 'vue-router';
import Home from '@/pages/Home';
import Blog from '@/pages/Blog';
import Page from '@/pages/Page';

Vue.use(Router);

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
    },
    {
      path: '/blog',
      name: 'Blog',
      component: Blog,
    },
    {
      path: '/blog/:slug',
      name: 'Page',
      component: Page,
    },
  ],
  scrollBehavior() {
    return { x: 0, y: 0 };
  },
});

Change router mode to 'history'


require('babel-register');

const serverConfig = require('./config');
const WebpackIsomorphicTools = require('webpack-isomorphic-tools');
const webpackIsomorphicAssets = require('../../webpack/assets');
const rootDir = require('path').resolve(__dirname, '..', '..');

// if (!process.env.NODE_ENV)
//   throw new Error('Environment variable NODE_ENV isn\'t set. Remember it\'s up your production enviroment to set NODE_ENV and maybe other variables.');

// http://formatjs.io/guides/runtime-environments/#polyfill-node
if (global.Intl) {
  // We don't have to check whether Node runtime supports specific language,
  // because without special build it does support only english anyway.
  require('intl');
  global.Intl.NumberFormat = global.IntlPolyfill.NumberFormat;
  global.Intl.DateTimeFormat = global.IntlPolyfill.DateTimeFormat;
} else {
  global.Intl = require('intl');
}

global.webpackIsomorphicTools = new WebpackIsomorphicTools(webpackIsomorphicAssets)
  .development(!serverConfig.isProduction)
  .server(rootDir, () => {
    require('./main');
  });

require('babel-register');

const serverConfig = require('./config');
const WebpackIsomorphicTools = require('webpack-isomorphic-tools');
const webpackIsomorphicAssets = require('../../webpack/assets');
const rootDir = require('path').resolve(__dirname, '..', '..');

if (!process.env.NODE_ENV)
  throw new Error(
    'Environment variable NODE_ENV must be set to development or production.'
  );

// http://formatjs.io/guides/runtime-environments/#polyfill-node
if (global.Intl) {
  // We don't have to check whether Node runtime supports specific language,
  // because without special build it does support only english anyway.
  require('intl');
  global.Intl.NumberFormat = global.IntlPolyfill.NumberFormat;
  global.Intl.DateTimeFormat = global.IntlPolyfill.DateTimeFormat;
} else {
  global.Intl = require('intl');
}

global.webpackIsomorphicTools = new WebpackIsomorphicTools(webpackIsomorphicAssets)
  .development(!serverConfig.isProduction)
  .server(rootDir, () => {
    require('./main');
  });

Put server process.env.NODE_ENV check back


/**
 * Swap reversed DateRange values
 */

var updated = 0;

// Match all Dataset having temporal_coverage.start > temporal_coverage.end
const pipeline = [
    {$project: {
        cmp: {$cmp: ['$temporal_coverage.start', '$temporal_coverage.end']},
        obj: '$$ROOT'
    }},
    {$match: {cmp: {$gt: 0}}},
    {$replaceRoot: {newRoot: '$obj'}}
];

db.dataset.aggregate(pipeline).forEach(dataset => {
    db.dataset.update(
        {_id: dataset._id},
        {'$set': {
            'temporal_coverage.start': dataset.temporal_coverage.end,
            'temporal_coverage.end': dataset.temporal_coverage.start,
        }}
    );
    updated++;
});

print(`Updated ${updated} datasets with reversed temporal coverage.`);

/**
 * Swap reversed DateRange values
 */

var updated = 0;

// Match all Dataset having temporal_coverage.start > temporal_coverage.end
const pipeline = [
    {$project: {
        cmp: {$cmp: ['$temporal_coverage.start', '$temporal_coverage.end']},
        dataset: '$$ROOT'
    }},
    {$match: {cmp: {$gt: 0}}}
];

db.dataset.aggregate(pipeline).forEach(row => {
    db.dataset.update(
        {_id: row.dataset._id},
        {'$set': {
            'temporal_coverage.start': row.dataset.temporal_coverage.end,
            'temporal_coverage.end': row.dataset.temporal_coverage.start,
        }}
    );
    updated++;
});

print(`Updated ${updated} datasets with reversed temporal coverage.`);

Fix reverse date range migration for Mongo 3.2


openerp.web_context_tunnel = function(instance) {

    instance.web.form.FormWidget.prototype.build_context = function() {
        var v_context = false;
        var fields_values = false;
        // instead of using just the attr context, we merge any attr starting with context
        for (var key in this.node.attrs) {
            if (key.substring(0, 7) === "context") {
                if (!v_context) {
                    fields_values = this.field_manager.build_eval_context();
                    v_context = new instance.web.CompoundContext(this.node.attrs[key]).set_eval_context(fields_values);
                } else {
                    v_context = new instance.web.CompoundContext(this.node.attrs[key], v_context).set_eval_context(fields_values);
                }

            }
        }
        if (!v_context) {
            v_context = (this.field || {}).context || {};
        }
        return v_context;
    };
};

// vim:et fdc=0 fdl=0:

openerp.web_context_tunnel = function(instance) {

    instance.web.form.FormWidget.prototype.build_context = function() {
        var v_context = false;
        var fields_values = false;
        // instead of using just the attr context, we merge any attr starting with context
        for (var key in this.node.attrs) {
            if (key.substring(0, 7) === "context") {
                if (!v_context) {
                    fields_values = this.field_manager.build_eval_context();
                    v_context = new instance.web.CompoundContext(this.node.attrs[key]).set_eval_context(fields_values);
                } else {
                    v_context = new instance.web.CompoundContext(this.node.attrs[key], v_context).set_eval_context(fields_values);
                }

            }
        }
        if (!v_context) {
            v_context = (this.field || {}).context || {};
            v_context = new instance.web.CompoundContext(v_context).set_eval_context(false);
        }
        return v_context;
    };
};

// vim:et fdc=0 fdl=0:

Fix lp:1302500 by evaluating an empty context to avoid error


"use strict";

var App = require('../app'),
    coreUtils = require('../core/utils'),
    models = require('./models'),
    views = require('./views');

var DataCatalogController = {
    dataCatalog: function() {
        App.map.setDataCatalogSize();

        App.state.set({
            'active_page': coreUtils.dataCatalogPageTitle,
        });

        var form = new models.SearchForm();

        var catalogs = new models.Catalogs([
            new models.Catalog({
                id: 'cinergi',
                name: 'CINERGI',
                active: true,
                results: new models.Results()
            }),
            new models.Catalog({
                id: 'hydroshare',
                name: 'HydroShare',
                results: new models.Results()
            }),
            new models.Catalog({
                id: 'cuahsi',
                name: 'WDC',
                description: 'Optional catalog description here...',
                results: new models.Results()
            })
        ]);

        var view = new views.DataCatalogWindow({
            model: form,
            collection: catalogs
        });
        App.rootView.sidebarRegion.show(view);
    }
};

module.exports = {
    DataCatalogController: DataCatalogController,
};

"use strict";

var App = require('../app'),
    router = require('../router').router,
    coreUtils = require('../core/utils'),
    models = require('./models'),
    views = require('./views');

var DataCatalogController = {
    dataCatalogPrepare: function() {
        if (!App.map.get('areaOfInterest')) {
            router.navigate('', { trigger: true });
            return false;
        }
    },

    dataCatalog: function() {
        App.map.setDataCatalogSize();

        App.state.set({
            'active_page': coreUtils.dataCatalogPageTitle,
        });

        var form = new models.SearchForm();

        var catalogs = new models.Catalogs([
            new models.Catalog({
                id: 'cinergi',
                name: 'CINERGI',
                active: true,
                results: new models.Results()
            }),
            new models.Catalog({
                id: 'hydroshare',
                name: 'HydroShare',
                results: new models.Results()
            }),
            new models.Catalog({
                id: 'cuahsi',
                name: 'WDC',
                description: 'Optional catalog description here...',
                results: new models.Results()
            })
        ]);

        var view = new views.DataCatalogWindow({
            model: form,
            collection: catalogs
        });
        App.rootView.sidebarRegion.show(view);
    }
};

module.exports = {
    DataCatalogController: DataCatalogController,
};

Exit /search if no area of interest


Stirrup.prototype.buildStaticFunctions = function(constructor) {
  var staticFuncs = this.getConfig().staticFuncs;
  for(var i = 0, len = staticFuncs.length; i < len; i++) {
    var def = staticFuncs[i];
    var staticFunc = null;

    //If using native promises, and native promises implements
    //the given function, save
    if(this.isNative && Promise[def.nativeName]) {
      staticFunc = Promise[def.nativeName];
    }

    //If the function doesn't exist natively, use the library
    if(!staticFunc) {
      staticFunc = this.library[def.libName];
    }

    if(def.aliases) {
      //Attach function to aliases
      var fLen = def.aliases.length;
      for(var f = 0; f < fLen; f++) {
        constructor[def.aliases[f]] = staticFunc;
      }
    } else {
      constructor[def.libName] = staticFunc;
    }
  }
};

Stirrup.prototype.buildStaticFunctions = function(constructor) {
  var staticFuncs = this.getConfig().staticFuncs;
  for(var i = 0, len = staticFuncs.length; i < len; i++) {
    var def = staticFuncs[i];
    var staticFunc = null;

    //If using native promises, and native promises implements
    //the given function, save
    if(this.isNative && Promise[def.nativeName]) {
      staticFunc = Promise[def.nativeName];
    } else if(this.library[def.libName]) { //If the function doesn't exist natively, use the library
      staticFunc = this.library[def.libName];
    } else {
      console.error('Bad config. Could not find native or library function for "' + def.libName + '"');
      return;
    }

    if(def.aliases) {
      //Attach function to aliases
      var fLen = def.aliases.length;
      for(var f = 0; f < fLen; f++) {
        constructor[def.aliases[f]] = staticFunc;
      }
    } else {
      constructor[def.libName] = staticFunc;
    }
  }
};

Fix logic and show proper error


var angular = require('angular');
var Board = require('../../../../src/HexBoard.js'); //
var paper = require('browserifyable-paper');
module.exports = angular.module( 'hexBoard', [] )

.directive( 'hexBoard', ['$window', function($window) {
  return {
    link: function( scope, element, attrs ) {

      scope.board = new Board(scope.hexDimensions, element[0], scope.contexts, scope.globalMouseClicked, $window);
            onResize = function (event){
                   paperview.viewSize = [event.size.width, event.size.height]
        }
      scope.$emit('boardInitialized');

    }
  };
}])

;

var angular = require('angular');
var Board = require('../../../../src/HexBoard.js'); //
module.exports = angular.module( 'hexBoard', [] )

.directive( 'hexBoard', ['$window', function($window) {
  return {
    link: function( scope, element, attrs ) {

      scope.board = new Board(scope.hexDimensions, element[0], scope.contexts, scope.globalMouseClicked, $window);
      scope.$emit('boardInitialized');

    }
  };
}])

;

Remove (hopefully final) references to browserifyable-paper


pjax_config_page("profit_table", function(){
    return {
        onLoad: function() {
            if (!getCookieItem("login")) {
                window.location = window.location.host + "/login";
                return;
            }

            BinarySocket.init({
                onmessage: function(msg){
                    var response = JSON.parse(msg.data);

                    if (response) {
                        var type = response.msg_type;
                        if (type === 'profit_table'){
                            ProfitTableWS.profitTableHandler(response);
                        }
                    }
                }
            });
            Content.populate();
            ProfitTableWS.init();
        },
        onUnload: function(){
            ProfitTableWS.clean();
        }
    };
});

pjax_config_page("profit_table", function(){
    return {
        onLoad: function() {
            BinarySocket.init({
                onmessage: function(msg){
                    var response = JSON.parse(msg.data);

                    if (response) {
                        var type = response.msg_type;
                        if (type === 'profit_table'){
                            ProfitTableWS.profitTableHandler(response);
                        }
                    }
                }
            });
            Content.populate();
            ProfitTableWS.init();
        },
        onUnload: function(){
            ProfitTableWS.clean();
        }
    };
});

Revert "client side redirect for unauth user"


var Hogan = require('hogan.js'),
    fs = require('fs'),
    path = require('path'),
    govukDir = path.normalize(__dirname + '/../govuk_modules'),
    govukConfig = require(__dirname + '/template-config'),
    compiledTemplate,
    govukTemplate,
    handleErr;

handleErr = function (err) {
  if (err) {
    throw err;
  }
};

module.exports = {
  convert : function () {
    govukTemplate = fs.readFileSync(govukDir + '/views/govuk_template.html', { encoding : 'utf-8' });
    compiledTemplate = Hogan.compile(govukTemplate);
    fs.writeFileSync(govukDir + '/views/govuk_template.html', compiledTemplate.render(govukConfig), { encoding : 'utf-8' });
  }
};

var Hogan = require('hogan.js'),
    fs = require('fs'),
    path = require('path'),
    govukDir = path.normalize(__dirname + '/../govuk_modules/govuk_template'),
    govukConfig = require(__dirname + '/template-config'),
    compiledTemplate,
    govukTemplate,
    handleErr;

handleErr = function (err) {
  if (err) {
    throw err;
  }
};

module.exports = {
  convert : function () {
    govukTemplate = fs.readFileSync(govukDir + '/views/layouts/govuk_template.html', { encoding : 'utf-8' });
    compiledTemplate = Hogan.compile(govukTemplate);
    fs.writeFileSync(govukDir + '/views/layouts/govuk_template.html', compiledTemplate.render(govukConfig), { encoding : 'utf-8' });
  }
};

Update path to convert template


// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.require('assets/frappe/js/lib/jscolor/jscolor.js');

frappe.provide("frappe.website_theme");
$.extend(frappe.website_theme, {
	color_variables: ["background_color", "top_bar_color", "top_bar_text_color",
		"footer_color", "footer_text_color", "text_color", "link_color"]
});

frappe.ui.form.on("Website Theme", "onload_post_render", function(frm) {
	$.each(frappe.website_theme.color_variables, function(i, v) {
		$(frm.fields_dict[v].input).addClass('color {required:false,hash:true}');
	});
	jscolor.bind();
});

frappe.ui.form.on("Website Theme", "refresh", function(frm) {
	frm.toggle_display(["module", "custom"], !!frappe.boot.developer_mode);
	if (!frm.doc.custom && !!!frappe.boot.developer_mode) {
		frm.set_read_only();
		frm.disable_save();
	}

});

// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.require('assets/frappe/js/lib/jscolor/jscolor.js');

frappe.provide("frappe.website_theme");
$.extend(frappe.website_theme, {
	color_variables: ["background_color", "top_bar_color", "top_bar_text_color",
		"footer_color", "footer_text_color", "text_color", "link_color"]
});

frappe.ui.form.on("Website Theme", "onload_post_render", function(frm) {
	$.each(frappe.website_theme.color_variables, function(i, v) {
		$(frm.fields_dict[v].input).addClass('color {required:false,hash:true}');
	});
	jscolor.bind();
});

frappe.ui.form.on("Website Theme", "refresh", function(frm) {
	frm.toggle_display(["module", "custom"], !frappe.boot.developer_mode);
	if (!frm.doc.custom && !frappe.boot.developer_mode) {
		frm.set_read_only();
		frm.disable_save();
	} else {
		frm.enable_save();
	}
});

Enable save button if custom and not in developer mode


import React from 'react';
import { FaTimes } from 'react-icons/fa';
import './CloseButton.css';

export default (
  <button
    className="close"
    onClick={() => {
      // eslint-disable-next-line no-undef
      var application = fin.desktop.Application.getCurrent();
      application.close();
    }}
  >
    <FaTimes />
  </button>
);

import React from 'react';
import { FaTimes } from 'react-icons/fa';
import './CloseButton.css';

export default (
  <button
    className="close"
    onClick={() => window.fin.Window.getCurrentSync().close()}
  >
    <FaTimes />
  </button>
);

Use simpler close button logic


define([
    'base/view',
    'modules/user/userDetailView',
    'hgn!modules/user/user',
    'component/eventBus'
], function(View, UserDetailView, userTemplate, events) {

    var UserView = View.extend({

        template: userTemplate,

        initialize: function(options) {
            this.user = options.user;

            // don't write this
            // this.user.on("change", this.render, this);

            // write this instead (see component/eventBinder.js)
            this.bindTo(this.user, "change", this.render, this);

            // or this to bind to global events
            this.bindTo(events, "global", this.render, this);
        },

        render: function() {
            this.renderTemplate(this.user.toJSON());
            this.renderUserDetail(this.$el.find(".user-detail"));
        },

        renderUserDetail: function(el) {
            this.userDetailView = this.userDetailView || new UserDetailView({ el: el });
            this.addSubView(this.userDetailView);
            this.userDetailView.render();
        }

    });

    return UserView;

});

define([
    'base/view',
    'modules/user/userDetailView',
    'hgn!modules/user/user',
    'component/eventBus'
], function(View, UserDetailView, userTemplate, events) {

    var UserView = View.extend({

        template: userTemplate,

        initialize: function(options) {
            this.user = options.user;

            // don't write this
            // this.user.on("change", this.render, this);

            // write this instead (see component/eventBinder.js)
            this.bindTo(this.user, "change", this.render, this);

            // or this to bind to global events
            this.bindTo(events, "global", this.render, this);
        },

        render: function() {
            this.renderTemplate(this.user.toJSON());
            this.renderUserDetail(this.$(".user-detail"));
        },

        renderUserDetail: function(el) {
            this.userDetailView = this.userDetailView || new UserDetailView({ el: el });
            this.addSubView(this.userDetailView);
            this.userDetailView.render();
        }

    });

    return UserView;

});

Use view.$ instead of view.$el.find


/**
 * Copyright 2003-present Greg Hurrell. All rights reserved.
 * Licensed under the terms of the MIT license.
 *
 * @flow
 */

export default function addDigits(
  aDigits: Array<number>,
  bDigits: Array<number>,
  base: number
): Array<number> {
  let result = [];
  let carry = 0;
  const aLength = aDigits.length;
  const bLength = bDigits.length;
  for (let i = 0; i < aLength || i < bLength || carry; i++) {
    const aDigit = i < aLength ? aDigits[aLength - i - 1] : 0;
    const bDigit = i < bLength ? bDigits[bLength - i - 1] : 0;
    const sum = aDigit + bDigit + carry;
    result.unshift(sum % base);

    // ~~ here is the equivalent of Math.floor; used to avoid V8 de-opt,
    // "Reference to a variable which requires dynamic lookup".
    carry = ~~(sum / base);
  }
  return result.length ? result : [0];
}

/**
 * Copyright 2003-present Greg Hurrell. All rights reserved.
 * Licensed under the terms of the MIT license.
 *
 * @flow
 */

export default function addDigits(
  aDigits: Array<number>,
  bDigits: Array<number>,
  base: number
): Array<number> {
  let result = [];
  let carry = 0;
  const aLength = aDigits.length;
  const bLength = bDigits.length;
  for (let i = 0; i < aLength || i < bLength || carry; i++) {
    const aDigit = i < aLength ? aDigits[aLength - i - 1] : 0;
    const bDigit = i < bLength ? bDigits[bLength - i - 1] : 0;
    const sum = aDigit + bDigit + carry;
    result.push(sum % base);

    // ~~ here is the equivalent of Math.floor; used to avoid V8 de-opt,
    // "Reference to a variable which requires dynamic lookup".
    carry = ~~(sum / base);
  }
  return result.length ? result.reverse() : [0];
}

Replace `unshift()` with `push()` plus `reverse()`


import React from 'react';
import PropTypes from 'prop-types';
import Link from 'gatsby-link';
import Helmet from 'react-helmet';
import { rhythm } from '../utils/typography';
import Navigation from '../components/navigation/navigation';
import Footer from '../components/footer/footer';
import '../css/main.css';

export default class Template extends React.Component {
  static propTypes = {
    children: PropTypes.func
  };
  render() {
    return (
      <main>
        <Helmet
          title="Quality Link Consulting"
          meta={[{ name: 'description', content: 'Sample' }, { name: 'keywords', content: 'sample, something' }]}
        />
        <Navigation />
        <div>{this.props.children()}</div>
        <Footer />
      </main>
    );
  }
}

import React from 'react';
import PropTypes from 'prop-types';
import Link from 'gatsby-link';
import Helmet from 'react-helmet';
import { rhythm } from '../utils/typography';
import Navigation from '../components/Navigation/navigation';
import Footer from '../components/footer/footer';
import '../css/main.css';

export default class Template extends React.Component {
  static propTypes = {
    children: PropTypes.func
  };
  render() {
    return (
      <main>
        <Helmet
          title="Quality Link Consulting"
          meta={[{ name: 'description', content: 'Sample' }, { name: 'keywords', content: 'sample, something' }]}
        />
        <Navigation />
        <div>{this.props.children()}</div>
        <Footer />
      </main>
    );
  }
}

Fix path to navigation component


//Dependencies.
var builder = require('focus').component.builder;
var React = require('react');
var type = require('focus').component.types;

/**
 * Input text mixin.
 * @type {Object}
 */
var displayTextMixin = {
  /** @inheritdoc */
  getDefaultProps: function getInputDefaultProps() {
    return {
      value: undefined,
      name: undefined,
      formatter: function(data){return data;},
      style: {}
    };
  },
  /** @inheritdoc */
  propTypes: {
    type: type('string'),
    value: type(['string', 'number']),
    name: type('string'),
    style: type('object')
  },
  renderValue: function renderValueDisplayText(){
      return this.props.formatter(this.props.value);
  },
  /**
   * Render a display field.
   * @return {DOM} - The dom of an input.
   */
  render: function renderInput() {
    return (
      <div
        id={this.props.name}
        name={this.props.name}
        className={this.props.style.class}
      >{this.renderValue()}</div>
    );
  }
};

module.exports = builder(displayTextMixin);

//Dependencies.
const builder = require('focus').component.builder;
const React = require('react');
const type = require('focus').component.types;

/**
 * Input text mixin.
 * @type {Object}
 */
const displayTextMixin = {
  /** @inheritdoc */
  getDefaultProps(){
    return {
      formatter: (data)=> data
    };
  },
  /** @inheritdoc */
  propTypes: {
    type: type('string'),
    value: type(['string', 'number']),
    name: type('string'),
    style: type('object')
  },
  /**
   * Render the value.
   * @return {string} The formated value.
   */
  renderValue(){
      const {formatter, value} = this.props;
      return formatter(value);
  },
   /** @inheritdoc */
  render: function renderInput() {
      return <div {...this.props}>{this.renderValue()}</div>;
  }
};

module.exports = builder(displayTextMixin);

Rewrite component and pass all props to the children.


(function (ng) {
  'use strict';

  /**
   * Angular.js utility for cleaner dependency injection.
   */
  var $inject = function (cls, self, args) {
    var i;
    var key;
    var str;
    var func;
    var depNames = [];
    var deps = [];
    var l = cls.$inject.length;

    // Inject all dependencies into the self reference.
    for (i = 0; i < l; i++) {
      key = '_' + cls.$inject[i];
      self[key] = args[i];
    }

    for (key in cls.prototype) {
      if (typeof cls.prototype[key] === 'function') {
        func = cls.prototype[key];
        str = func.toString();

        // List of dependencies.
        depNames = str.match(/\/\*\s*\$inject:([^*]+)/);

        // Skip methods without the $inject comment.
        if (depNames.length < 2) continue;

        depNames = depNames[1].split(',');

        // Map the dependency names to the actual dependencies.
        args = depNames.map(function (name) {
          return self['_' + name.trim()];
        });

        // Add the "this" value to the arguments list.
        args.unshift(func);

        self[key] = func.bind.apply(func, args);
      }
    }
  };

  var $injectService = function () {
    return $inject;
  };

  ng.module('cg.inject', [])
    .factory('$inject', $injectService);
})(angular);

(function (ng) {
  'use strict';

  /**
   * Angular.js utility for cleaner dependency injection.
   */
  var $inject = function (cls, self, args) {
    var i;
    var key;
    var str;
    var func;
    var depNames = [];
    var deps = [];
    var l = cls.$inject.length;

    // Inject all dependencies into the self reference.
    for (i = 0; i < l; i++) {
      key = '_' + cls.$inject[i];
      self[key] = args[i];
    }

    for (key in cls.prototype) {
      if (typeof cls.prototype[key] === 'function') {
        func = cls.prototype[key];
        str = func.toString();

        // List of dependencies.
        depNames = str.match(/\/\*\s*\$inject:([^*]+)/);

        // Skip methods without the $inject comment.
        if (depNames && depNames.length < 2) continue;

        depNames = depNames[1].split(',');

        // Map the dependency names to the actual dependencies.
        args = depNames.map(function (name) {
          return self['_' + name.trim()];
        });

        // Add the "this" value to the arguments list.
        args.unshift(func);

        self[key] = func.bind.apply(func, args);
      }
    }
  };

  var $injectService = function () {
    return $inject;
  };

  ng.module('cg.inject', [])
    .factory('$inject', $injectService);
})(angular);

Fix bug when no DI for methods is used


const path = require('path');
const { getLoader } = require('react-app-rewired');

function createRewireLess(lessLoaderOptions = {}) {
  return function(config, env) {
    const lessExtension = /\.less$/;

    const fileLoader = getLoader(
      config.module.rules,
      rule =>
        rule.loader &&
        typeof rule.loader === 'string' &&
        rule.loader.endsWith(`file-loader${path.sep}index.js`)
    );
    fileLoader.exclude.push(lessExtension);

    const cssRules = getLoader(
      config.module.rules,
      rule => String(rule.test) === String(/\.css$/)
    );

    let lessRules;
    if (env === 'production') {
      lessRules = {
        test: lessExtension,
        loader: [
          // TODO: originally this part is wrapper in extract-text-webpack-plugin
          //       which we cannot do, so some things like relative publicPath
          //       will not work.
          //       https://github.com/timarney/react-app-rewired/issues/33
          ...cssRules.loader,
          { loader: 'less-loader', options: lessLoaderOptions }
        ]
      };
    } else {
      lessRules = {
        test: lessExtension,
        use: [
          ...cssRules.use,
          { loader: 'less-loader', options: lessLoaderOptions }
        ]
      };
    }

    config.module.rules.push(lessRules);

    return config;
  };
}

const rewireLess = createRewireLess();

rewireLess.withLoaderOptions = createRewireLess;

module.exports = rewireLess;

const path = require('path');
const { getLoader } = require('react-app-rewired');

function createRewireLess(lessLoaderOptions = {}) {
  return function(config, env) {
    const lessExtension = /\.less$/;

    const fileLoader = getLoader(
      config.module.rules,
      rule =>
        rule.loader &&
        typeof rule.loader === 'string' &&
        rule.loader.indexOf(`${path.sep}file-loader${path.sep}`) !== -1,
    );
    fileLoader.exclude.push(lessExtension);

    const cssRules = getLoader(
      config.module.rules,
      rule => String(rule.test) === String(/\.css$/)
    );

    let lessRules;
    if (env === 'production') {
      lessRules = {
        test: lessExtension,
        loader: [
          // TODO: originally this part is wrapper in extract-text-webpack-plugin
          //       which we cannot do, so some things like relative publicPath
          //       will not work.
          //       https://github.com/timarney/react-app-rewired/issues/33
          ...cssRules.loader,
          { loader: 'less-loader', options: lessLoaderOptions }
        ]
      };
    } else {
      lessRules = {
        test: lessExtension,
        use: [
          ...cssRules.use,
          { loader: 'less-loader', options: lessLoaderOptions }
        ]
      };
    }

    config.module.rules.push(lessRules);

    return config;
  };
}

const rewireLess = createRewireLess();

rewireLess.withLoaderOptions = createRewireLess;

module.exports = rewireLess;

Fix file-loader exclusion in react-app-rewire-less


/* eslint-env mocha */
const extendedEuclidean = require('../../../src').algorithms.math.extendedEuclidean;

const assert = require('assert');

describe('Extended Euclidean', () => {
  it('should check for both numbers as 0', () => {
    assert.deepStrictEqual(extendedEuclidean(35, 15), { gcd: 5, x: 1, y: -2 });
  });
});

/* eslint-env mocha */
const extendedEuclidean = require('../../../src').algorithms.math.extendedEuclidean;

const assert = require('assert');

describe('Extended Euclidean', () => {
  it('should check for numbers as 0', () => {
    assert.deepStrictEqual(extendedEuclidean(35, 0), { gcd: 35, x: 1, y: 0 });
    assert.deepStrictEqual(extendedEuclidean(0, 15), { gcd: 15, x: 0, y: 1 });
    assert.deepStrictEqual(extendedEuclidean(0, 0), { gcd: 0, x: 0, y: 1 });
  });

  it('should check extended euclidean values for positive values of a and b', () => {
    assert.deepStrictEqual(extendedEuclidean(35, 15), { gcd: 5, x: 1, y: -2 });
    assert.deepStrictEqual(extendedEuclidean(32, 1), { gcd: 1, x: 0, y: 1 });
    assert.deepStrictEqual(extendedEuclidean(1, 32), { gcd: 1, x: 1, y: 0 });
  });

  it('should check extended euclidean values for negative values of a and b', () => {
    assert.deepStrictEqual(extendedEuclidean(35, -15), { gcd: 5, x: 1, y: 2 });
    assert.deepStrictEqual(extendedEuclidean(-35, 15), { gcd: -5, x: 1, y: 2 });
    assert.deepStrictEqual(extendedEuclidean(-35, -15), { gcd: -5, x: 1, y: -2 });
  });
});

Test Extended Euclidean: Add more test cases


var React = require('react');

var Character = React.createClass({
  getThumbnail: function() {
    var image = this.props.character.thumbnail.path+'.'+this.props.character.thumbnail.extension;

    return (

    )
  },

  getName: function() {
    return (

        {this.props.character.name}

    );
  },

  getDescription: function() {
    var description = this.props.character.description ? this.props.character.description : 'No description'

    return (

        {description}

    )
  },

  getLinks: function() {
    return (

        Wiki|
        More details

    )
  },

  render: function () {
    return (

              {this.getThumbnail()}

                {this.getName()}

                {this.getDescription()}
                {this.getLinks()}

    );
  }
});

module.exports = Character;

var React = require('react');

var Character = React.createClass({
  getThumbnail: function() {
    var image = 'http://placehold.it/250x250';

    if(this.props.character.thumbnail) {
      image = this.props.character.thumbnail.path+'.'+this.props.character.thumbnail.extension;
    }

    return (

    )
  },

  getName: function() {
    return (

        {this.props.character.name}

    );
  },

  getDescription: function() {
    var description = this.props.character.description ? this.props.character.description : 'No description'

    return (

        {description}

    )
  },

  getLinks: function() {
    return (

        Wiki|
        More details

    )
  },

  render: function () {
    return (

              {this.getThumbnail()}

                {this.getName()}

                {this.getDescription()}
                {this.getLinks()}

    );
  }
});

module.exports = Character;

Fix the error when there's no thumbnail available


module.exports = function () {
  return {
    delivery: ['meta'],
    policy: {
      'default-src': ["'none'"],
      'script-src': ["'self'"],
      'font-src': ["'self'"],
      'connect-src': ["'self'"],
      'img-src': [
        "'self'",
        // Bootstrap 4 uses data URL for some SVG images in CSS
        'data:',
      ],
      'style-src': ["'self'"],
      'media-src': ["'self'"],
    },
  };
};

module.exports = function () {
  return {
    delivery: ['meta'],
    policy: {
      'default-src': ["'none'"],
      'script-src': ["'self'"],
      'font-src': ["'self'"],
      'connect-src': ["'self'"],
      'img-src': [
        "'self'",
        // Bootstrap 4 uses data URL for some SVG images in CSS
        'data:',
      ],
      'style-src': [
        "'self'",
        "'sha256-TfKF8gl0QHp566h8GzfYjTsiRn3j9Ll7S0zdoLOq7ic='",
        "'sha256-9jmMc10d3gtj1r8cOJX+x+p1bMOS+vX+i8iVCr3h18A='",
      ],
      'media-src': ["'self'"],
    },
  };
};

Fix CSP failures in FastBoot tests


/*jshint multistr: true, unused: false*/

'use strict';

var $ = require('jquery'),
    PulsarSortableComponent = require('../../../js/PulsarSortableComponent');

$.fx.off = !$.fx.off;

describe('Sortable component', function() {

    beforeEach(function() {
        this.$html = $('

').appendTo('html');
        this.$body = $('

').appendTo(this.$html);
        this.$markup = $('\

\

\

foo
\

\

\
').appendTo(this.$html);

        this.$table = this.$html.find('.table');

        this.pulsarSortableComponent = new PulsarSortableComponent(this.$html);
    });

    describe('Initialising the sortable component with a sortable table', function() {

        beforeEach(function() {
            sinon.spy($.fn, 'sortable');
            this.pulsarSortableComponent.init();
        });

        it('should call the sortable plugin', function() {
            expect($.fn.sortable).to.have.been.called;
        });

    });

});

/*jshint multistr: true, unused: false*/

'use strict';

var $ = require('jquery'),
    PulsarSortableComponent = require('../../../js/PulsarSortableComponent');

$.fx.off = !$.fx.off;

describe('Sortable component', function() {

    beforeEach(function() {
        this.$html = $('

').appendTo('html');
        this.$body = $('

').appendTo(this.$html);
        this.$markup = $('\

\

\

foo
\

\

\
').appendTo(this.$html);

        this.$table = this.$html.find('.table.is-sortable tbody');

        this.pulsarSortableComponent = new PulsarSortableComponent(this.$html);
    });

    describe('Initialising the sortable component with a sortable table', function() {

        beforeEach(function() {
            sinon.spy($.fn, 'sortable');
            this.pulsarSortableComponent.init();
        });

        it('should call the sortable plugin', function() {
            expect($.fn.sortable).to.have.been.called;
        });

    });

});

Tweak sortable selector for sortable tests


// $Revision$

Ensembl.Panel.PopulationGraph = Ensembl.Panel.Piechart.extend({
  init: function () {
    // Allele colours
    this.graphColours = {
      'A'       : '#00BB00',
      'T'       : '#FF0000',
      'G'       : '#FFD700',
      'C'       : '#0000FF',
      'default' : [ '#222222', '#FF00FF', '#008080', '#7B68EE' ] // Other colours if the allele is not A, T, G or C
    };

    this.base();
  },

  toggleContent: function (el) {
    if (el.hasClass('open') && !el.data('done')) {
      this.base(el);
      this.makeGraphs($('.pie_chart > div', '.' + el.attr('rel')).map(function () { return this.id.replace('graphHolder', ''); }).toArray());
      el.data('done', true);
    } else {
      this.base(el);
    }

    el = null;
  }
});

// $Revision$

Ensembl.Panel.PopulationGraph = Ensembl.Panel.Piechart.extend({
  init: function () {
    // Allele colours
    this.graphColours = {
      'A'       : '#00A000',
      'T'       : '#FF0000',
      'G'       : '#FFCC00',
      'C'       : '#0000FF',
      '-'       : '#000000',
      'default' : [ '#008080', '#FF00FF', '#7B68EE' ] // Other colours if the allele is not A, T, G, C or -
    };

    this.base();
  },

  toggleContent: function (el) {
    if (el.hasClass('open') && !el.data('done')) {
      this.base(el);
      this.makeGraphs($('.pie_chart > div', '.' + el.attr('rel')).map(function () { return this.id.replace('graphHolder', ''); }).toArray());
      el.data('done', true);
    } else {
      this.base(el);
    }

    el = null;
  }
});

Change the colours of the 'A' and 'G' to make them slightly darker and them more consistent with the colours of the genotypes on the website


var CoreModule = require("substance/writer").CoreModule;

var ArchivistModule = require("./archivist");
var SubjectsModule = require("./subjects");
var EntitiesModule = require("./entities");
var MetadataModule = require("./metadata");
var TimecodesModule = require("./timecodes");

module.exports = [
  ArchivistModule,
  EntitiesModule,
  SubjectsModule,
  TimecodesModule,
  CoreModule,
  MetadataModule
];

var CoreModule = require("substance/writer").CoreModule;

var ArchivistModule = require("./archivist");
var SubjectsModule = require("./subjects");
var EntitiesModule = require("./entities");
var MetadataModule = require("./metadata");
var TimecodesModule = require("./timecodes");

module.exports = [
  ArchivistModule,
  EntitiesModule,
  SubjectsModule,
  TimecodesModule,
  CoreModule// ,
  // MetadataModule
];

Disable metadata tool until ready.


'use strict';
/*global jasmine:true */

var
    clean    = require('./lib/clean'),
    middle   = require('@whitneyit/middle'),

    gulp     = require('gulp'),
    eslint   = require('gulp-eslint'),
    istanbul = require('gulp-istanbul'),
    jasmine  = require('gulp-jasmine'),
    jscs     = require('gulp-jscs'),
    serve    = require('gulp-serve');

gulp.task('clean:coverage', function (done) {
    clean('coverage', done);
});

gulp.task('lint', function () {
    return gulp.src(['gulpfile.js', 'index.js', 'lib/**/*.js', 'tests/**/*.js'])
        .pipe(eslint())
        .pipe(eslint.format())
        .pipe(jscs());
});

gulp.task('serve:coverage', serve({
    'middleware' : middle('coverage'),
    'port'       : 3333,
    'root'       : 'coverage'
}));

gulp.task('test', ['clean:coverage'], function (done) {
    gulp.src(['lib/**/*.js'])
        .pipe(istanbul())
        .pipe(istanbul.hookRequire())
        .on('finish', function () {
            gulp.src(['tests/specs/**/*.spec.js'])
                .pipe(jasmine())
                .pipe(istanbul.writeReports({
                    'dir'       : 'coverage/lcov',
                    'reporters' : ['lcov', 'text-summary']
                }))
                .on('end', done);
        });
});

gulp.task('default', ['lint', 'test']);

'use strict';
/*global jasmine:true */

var
    clean    = require('./lib/clean'),
    middle   = require('@whitneyit/middle'),

    gulp     = require('gulp'),
    eslint   = require('gulp-eslint'),
    istanbul = require('gulp-istanbul'),
    jasmine  = require('gulp-jasmine'),
    jscs     = require('gulp-jscs'),
    serve    = require('gulp-serve');

gulp.task('clean:coverage', function (done) {
    clean('coverage', done);
});

gulp.task('lint', function () {
    return gulp.src(['gulpfile.js', 'index.js', 'lib/**/*.js', 'tests/**/*.js'])
        .pipe(eslint())
        .pipe(eslint.format())
        .pipe(jscs());
});

gulp.task('serve:coverage', serve({
    'middleware' : middle('coverage'),
    'port'       : 3333,
    'root'       : 'coverage'
}));

gulp.task('test', ['clean:coverage'], function (done) {
    gulp.src(['lib/**/*.js'])
        .pipe(istanbul({
            'includeUntested' : true
        }))
        .pipe(istanbul.hookRequire())
        .on('finish', function () {
            gulp.src(['tests/specs/**/*.spec.js'])
                .pipe(jasmine())
                .pipe(istanbul.writeReports({
                    'dir'       : 'coverage/lcov',
                    'reporters' : ['lcov', 'text-summary']
                }))
                .on('end', done);
        });
});

gulp.task('default', ['lint', 'test']);

Update `istanbul` to report on `untested` code


// Include gulp
var gulp = require('gulp');

// Include plugins
var concat = require('gulp-concat');

// Concatenate JS Files
gulp.task('scripts', function() {
    return gulp.src(['app/bower_components/mousetrap/mousetrap.js',
                     'app/bower_components/angular-hotkeys/src/hotkeys.js',
                     'src/*.js'])
      .pipe(concat('helmsman.js'))
      .pipe(gulp.dest('build'));
});

// Copy html files
gulp.task('html', function() {
    return gulp.src(['src/*.html'])
      .pipe(gulp.dest('build'));
});

// Default Task
gulp.task('default', ['scripts', 'html']);

// Include gulp
var gulp = require('gulp'),
    watch = require('gulp-watch');

// Include plugins
var concat = require('gulp-concat');

// Concatenate JS Files
gulp.task('scripts', function() {
    return watch(['app/bower_components/mousetrap/mousetrap.js',
                     'app/bower_components/angular-hotkeys/src/hotkeys.js',
                  'src/*.js'], {ignoreInitial: false})
      .pipe(concat('helmsman.js'))
      .pipe(gulp.dest('build'));
});

// Copy html files
gulp.task('html', function() {
    return watch('src/*.html', {ignoreInitial: false})
      .pipe(gulp.dest('build'));
});

// Default Task
gulp.task('default', ['scripts', 'html']);

Add gulp-watch ability to auto build


 /* exported trace */

// Logging utility function.
function trace(arg) {
  var now = (window.performance.now() / 1000).toFixed(3);
  console.log(now + ': ', arg);
}

 /* exported trace */

// Logging utility function.
function trace(arg) {
  const now = (window.performance.now() / 1000).toFixed(3)
  console.log(`${now}: `, arg);
}

Update trace function to template string.


//.CommonJS
var CSSOM = {};
///CommonJS

/**
 * @constructor
 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
 *
 * TODO: add if needed
 */
CSSOM.CSSValue = function CSSValue() {
};

CSSOM.CSSValue.prototype = {
	constructor: CSSOM.CSSValue,

	// @see: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
	set cssText() {
		var name = this._getConstructorName();

		throw new Exception('DOMException: property "cssText" of "' + name + '" is readonly!');
	},

	get cssText() {
		var name = this._getConstructorName();

		throw new Exception('getter "cssText" of "' + name + '" is not implemented!');
	},

	_getConstructorName: function() {
		var s = this.constructor.toString(),
				c = s.match(/function\s([^\(]+)/),
				name = c[1];

		return name;
	}
};

//.CommonJS
exports.CSSValue = CSSOM.CSSValue;
///CommonJS

//.CommonJS
var CSSOM = {};
///CommonJS

/**
 * @constructor
 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
 *
 * TODO: add if needed
 */
CSSOM.CSSValue = function CSSValue() {
};

CSSOM.CSSValue.prototype = {
	constructor: CSSOM.CSSValue,

	// @see: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
	set cssText(text) {
		var name = this._getConstructorName();

		throw new Exception('DOMException: property "cssText" of "' + name + '" is readonly!');
	},

	get cssText() {
		var name = this._getConstructorName();

		throw new Exception('getter "cssText" of "' + name + '" is not implemented!');
	},

	_getConstructorName: function() {
		var s = this.constructor.toString(),
				c = s.match(/function\s([^\(]+)/),
				name = c[1];

		return name;
	}
};

//.CommonJS
exports.CSSValue = CSSOM.CSSValue;
///CommonJS

Fix Firefox 18.0.2 complaining about a missing parameter for a setter


import React from 'react';
import LoadingSpinner from './LoadingSpinner';

class IFrame extends React.Component {
  constructor() {
    super();
    this.state = {
      loaded: false,
      height: 400
    };
  }

  componentDidMount() {
    this.refs.iframe.addEventListener('load', () => this.onLoad());
  }

  onLoad() {
    const height = this.refs.iframe.contentDocument.body.scrollHeight;
    this.setState({ loaded: true, height });
  }

  render() {
    let loading;
    if (!this.state.loaded) loading = <LoadingSpinner />;

    return (

        {loading}

    );
  }
}

IFrame.propTypes = {
  /**
   * The source url to load iframe
   */
  src: React.PropTypes.string.isRequired,
};

export default IFrame;

import React from 'react';
import LoadingSpinner from './LoadingSpinner';

class IFrame extends React.Component {
  constructor() {
    super();
    this.state = {
      loaded: false,
      height: 400
    };
  }

  componentDidMount() {
    this.refs.iframe.addEventListener('load', () => this.onLoad());
  }

  componentWillUnmount() {
    this.refs.iframe.removeEventListener('load', () => this.onLoad());
  }

  onLoad() {
    const height = this.refs.iframe.contentDocument.body.scrollHeight;
    this.setState({ loaded: true, height });
  }

  render() {
    let loading;
    if (!this.state.loaded) loading = <LoadingSpinner />;

    return (

        {loading}

    );
  }
}

IFrame.propTypes = {
  /**
   * The source url to load iframe
   */
  src: React.PropTypes.string.isRequired,
};

export default IFrame;

Remove listener on component unmount


// @flow

import pushUniqueVariable from '../helpers/pushUniqueVariable'

type Context = {
  [number]: Array<VariableName>,
}

function getVariablesInContext(context: Context, column: number) {
  return Object.keys(context)
    .filter(key => Number(key) <= column)
    // $FlowFixMe
    .map(key => context[key])
    .reduce((flattenArray, array) => flattenArray.concat(array), [])
}

export default class State {
  variables: VariableList
  context: Context

  constructor() {
    // Array of variable Nodes
    this.variables = []

    // Map of variable names
    this.context = {}
  }

  getVariables() {
    return this.variables
  }

  addVariables(column: number, variables: VariableList) {
    const contextVars = getVariablesInContext(this.context, column)

    variables
      .filter(variable => contextVars.indexOf(variable.value) === -1)
      .forEach((variable) => {
        this.variables = [...this.variables, variable]
      })
  }

  addToContext(column: number, variables: Array<VariableName>) {
    if (variables.length > 0) {
      if (this.context[column]) {
        this.context[column] = pushUniqueVariable(this.context[column], variables)
      } else {
        this.context[column] = variables
      }
    }
  }

  clearContext(column: number) {
    Object.keys(this.context)
      .filter(key => Number(key) > column)
      .forEach((key) => {
        // $FlowFixMe
        this.context[key] = []
      })
  }
}

Make sure to support new linter