function ResponsesNewWindow(surveyID) {
  try {
  var ResponsesIndexView = require('ui/common/responses/ResponsesIndexView');
  var ResponsesNewView = require('ui/common/responses/ResponsesNewView');
  var ConfirmDialog = require('ui/common/components/ConfirmDialog');

  var self = Ti.UI.createWindow({
    navBarHidden : true,
    backgroundColor : "#fff"
  });
  var view = new ResponsesNewView(surveyID);
  self.add(view);

  view.addEventListener('ResponsesNewView:savedResponse', function() {
    Ti.App.fireEvent('ResponseNewWindow:closed');
    view.cleanup();
    view = null;
    self.close();
  });

  var confirmDialog = new ConfirmDialog(L("confirm"), L("confirm_clear_answers"), onConfirm = function(e) {
    view.cleanup();
    view = null;
    self.close();
  });

  self.addEventListener('android:back', function() {
    confirmDialog.show();
  });

  return self;
  }
  catch(e) {
    var auditor = require('helpers/Auditor');
    auditor.writeIntoAuditFile(arguments.callee.name + " - " + e.toString());
  }
}

module.exports = ResponsesNewWindow;

function ResponsesNewWindow(surveyID) {
  try {
  var ResponsesIndexView = require('ui/common/responses/ResponsesIndexView');
  var ResponsesNewView = require('ui/common/responses/ResponsesNewView');
  var ConfirmDialog = require('ui/common/components/ConfirmDialog');

  var self = Ti.UI.createWindow({
    navBarHidden : true,
    backgroundColor : "#fff"
  });
  var view = new ResponsesNewView(surveyID);
  self.add(view);

  view.addEventListener('ResponsesNewView:savedResponse', function() {
    Ti.App.fireEvent('ResponseNewWindow:closed');
    view.cleanup();
    view = null;
    self.close();
  });

  var confirmDialog = new ConfirmDialog(L("confirm"), L("confirm_clear_answers"), onConfirm = function(e) {
    if(view) {
    view.cleanup();
  }
    view = null;
    self.close();
  });

  self.addEventListener('android:back', function() {
    confirmDialog.show();
  });

  return self;
  }
  catch(e) {
    var auditor = require('helpers/Auditor');
    auditor.writeIntoAuditFile(arguments.callee.name + " - " + e.toString());
  }
}

module.exports = ResponsesNewWindow;

Add a guard clause to ResponseNewWindow


// This module is responsible for capturing videos

'use strict';

const config = require('config');
const PiCamera = require('pi-camera');
const myCamera = new PiCamera({
  mode: 'video',
  output: process.argv[2],
  width: config.get('camera.videoWidth'),
  height: config.get('camera.videoHeight'),
  timeout: config.get('camera.videoTimeout'),
  nopreview: true,
});

process.on('message', (message) => {
  if (message.cmd === 'set') {
    myCamera.config = Object.assign(myCamera.config, message.set);
  }
  else if (message.cmd === 'capture') {
    myCamera.record()
      .then((result) => process.send({
        response: 'success',
        result,
        error: null,
      }))
      .catch((error) => process.send({
        response: 'failure',
        error,
      }));
  }
});

// This module is responsible for capturing videos

'use strict';

const config = require('config');
const PiCamera = require('pi-camera');
const myCamera = new PiCamera({
  mode: 'video',
  output: process.argv[2],
  width: config.get('camera.videoWidth'),
  height: config.get('camera.videoHeight'),
  timeout: config.get('camera.videoTimeout'),
  nopreview: true,
});

process.on('message', (message) => {
  if (message.cmd === 'set') {
    myCamera.config = Object.assign(myCamera.config, message.set);
  }
  else if (message.cmd === 'capture') {
    setTimeout(() => {
      myCamera.record()
        .then((result) => process.send({
          response: 'success',
          result,
          error: null,
        }))
        .catch((error) => process.send({
          response: 'failure',
          error,
        }));
    }, 500);
  }
});

Add minor delay to video capture. Hopefully this helps the camera moduel keep up.


const propertiesReader = require("properties-reader");

const defaults = {
  "primary-color": "#1890ff",
  "info-color": "#1890ff",
  "link-color": "#1890ff",
  "font-size-base": "14px",
  "border-radius-base": "2px",
};

function formatAntStyles() {
  const custom = {};
  const re = /styles.ant.([\w+-]*)/;

  try {
    const properties = propertiesReader("/etc/irida/irida.conf");
    properties.each((key, value) => {
      const found = key.match(re);
      if (found) {
        custom[found[1]] = value;
      }
    });
  } catch (e) {
    console.log("No styles in `/etc/irida/irida.conf`");
  }
  return Object.assign({}, defaults, custom);
}

module.exports = { formatAntStyles };

const propertiesReader = require("properties-reader");
const fs = require("fs");

const defaults = {
  "primary-color": "#1890ff",
  "info-color": "#1890ff",
  "link-color": "#1890ff",
  "font-size-base": "14px",
  "border-radius-base": "2px",
};
const iridaConfig = "/etc/irida/irida.conf";
const propertiesConfig = "../resources/configuration.properties";

function formatAntStyles() {
  const colourProperties = {};
  const re = /styles.ant.([\w+-]*)/;

  try {
    if (fs.existsSync(propertiesConfig)) {
      const properties = propertiesReader(propertiesConfig);
      properties.each((key, value) => {
        const found = key.match(re);
        if (found) {
          colourProperties[found[1]] = value;
        }
      });
    }

    if (fs.existsSync(iridaConfig)) {
      const properties = propertiesReader(iridaConfig);
      properties.each((key, value) => {
        const found = key.match(re);
        if (found) {
          colourProperties[found[1]] = value;
        }
      });
    }
  } catch (e) {
    console.log("No styles in `/etc/irida/irida.conf`");
  }
  return Object.assign({}, defaults, colourProperties);
}

module.exports = { formatAntStyles };

Check to see what values are in which file.


/*
 *
 * This is used to build the bundle with browserify.
 *
 * The bundle is used by people who doesn't use browserify.
 * Those who use browserify will install with npm and require the module,
 * the package.json file points to index.js.
 */

import Auth0Lock from './classic';
import Auth0LockPasswordless from './passwordless';

global.window.Auth0Lock = Auth0Lock;
global.window.Auth0LockPasswordless = Auth0LockPasswordless;
//use amd or just throught to window object.
if (typeof global.window.define == 'function' && global.window.define.amd) {
  global.window.define('auth0Lock', function () { return Auth0Lock; });
  global.window.define('auth0LockPasswordless', function () { return Auth0LockPasswordless; });
} else if (global.window) {
  global.window.Auth0Lock = Auth0Lock;
  global.window.Auth0LockPasswordless = Auth0LockPasswordless;
}

/*
 *
 * This is used to build the bundle with browserify.
 *
 * The bundle is used by people who doesn't use browserify.
 * Those who use browserify will install with npm and require the module,
 * the package.json file points to index.js.
 */

import Auth0Lock from './classic';
// import Auth0LockPasswordless from './passwordless';

global.window.Auth0Lock = Auth0Lock;
// global.window.Auth0LockPasswordless = Auth0LockPasswordless;
//use amd or just throught to window object.
if (typeof global.window.define == 'function' && global.window.define.amd) {
  global.window.define('auth0Lock', function () { return Auth0Lock; });
//  global.window.define('auth0LockPasswordless', function () { return Auth0LockPasswordless; });
} else if (global.window) {
  global.window.Auth0Lock = Auth0Lock;
//  global.window.Auth0LockPasswordless = Auth0LockPasswordless;
}

Exclude passwordless stuff from build


Align with changes in OverviewTable.


/*
 * Author: Alexandre Havrileck (Oxyno-zeta)
 * Date: 25/06/16
 * Licence: See Readme
 */
(function () {
	'use strict';

	angular
		.module('libparsio.technical.services')
		.factory('updateService', updateService);

	/** @ngInject */
	function updateService($q, updateDaoService, updateWrapperService, updateCacheService) {
		var service = {
			initialize: initialize
		};
		return service;

		////////////////

		/* ************************************* */
		/* ********  PRIVATE FUNCTIONS  ******** */
		/* ************************************* */

		/* ************************************* */
		/* ********   PUBLIC FUNCTIONS  ******** */
		/* ************************************* */

		/**
		 * Initialize.
		 */
		function initialize() {
			var promises = [];

			promises.push(updateDaoService.getReleases());
			promises.push(updateWrapperService.getAppVersion());

			$q.all(promises).then(function(response){
				var release = response[0];
				//var appVersion = response[1];
				var appVersion = '0.1.0';

				var version = updateWrapperService.clean(release['tag_name']);

				// Check if version if greater than actual
				var isGreaterThan = updateWrapperService.isGreaterThan(version, appVersion);
				if (isGreaterThan){
					updateCacheService.putAllData(true, version);
				}
			});
		}
	}

})();

/*
 * Author: Alexandre Havrileck (Oxyno-zeta)
 * Date: 25/06/16
 * Licence: See Readme
 */
(function () {
	'use strict';

	angular
		.module('libparsio.technical.services')
		.factory('updateService', updateService);

	/** @ngInject */
	function updateService($q, updateDaoService, updateWrapperService, updateCacheService) {
		var service = {
			initialize: initialize
		};
		return service;

		////////////////

		/* ************************************* */
		/* ********  PRIVATE FUNCTIONS  ******** */
		/* ************************************* */

		/* ************************************* */
		/* ********   PUBLIC FUNCTIONS  ******** */
		/* ************************************* */

		/**
		 * Initialize.
		 */
		function initialize() {
			var promises = [];

			promises.push(updateDaoService.getReleases());
			promises.push(updateWrapperService.getAppVersion());

			$q.all(promises).then(function(response){
				var release = response[0];
				var appVersion = response[1];

				var version = updateWrapperService.clean(release['tag_name']);

				// Check if version if greater than actual
				var isGreaterThan = updateWrapperService.isGreaterThan(version, appVersion);
				if (isGreaterThan){
					updateCacheService.putAllData(true, version);
				}
			});
		}
	}

})();

Remove mock version in code


exports.map = {
	'node': ['net.env.node.stdio'],
	'browser': ['net.env.browser.csp'],
	'mobile': []
}

exports.resolve = function(env, opts) {
	return exports.map[env] || [];
};

exports.map = {
	'node': [
		'net.env.node.stdio'
	],
	'browser': [
		'net.env.browser.csp',
		'net.env.browser.postmessage'
	],
	'mobile': []
}

exports.resolve = function(env, opts) {
	return exports.map[env] || [];
};

Fix for missing dynamic import


/**
  This view is used for rendering the buttons at the footer of the topic

  @class TopicFooterButtonsView
  @extends Discourse.ContainerView
  @namespace Discourse
  @module Discourse
**/
Discourse.TopicFooterButtonsView = Discourse.ContainerView.extend({
  elementId: 'topic-footer-buttons',
  topicBinding: 'controller.content',

  init: function() {
    this._super();
    this.createButtons();
  },

  // Add the buttons below a topic
  createButtons: function() {
    var topic = this.get('topic');
    if (Discourse.User.current()) {
      if (!topic.get('isPrivateMessage')) {
        // We hide some controls from private messages
        if (this.get('topic.details.can_invite_to')) {
          this.attachViewClass(Discourse.InviteReplyButton);
        }
        this.attachViewClass(Discourse.StarButton);
        this.attachViewClass(Discourse.ShareButton);
        this.attachViewClass(Discourse.ClearPinButton);
        if (this.get('topic.details.can_flag_topic')) {
          this.attachViewClass(Discourse.FlagTopicButton);
        }
      }
      if (this.get('topic.details.can_create_post')) {
        this.attachViewClass(Discourse.ReplyButton);
      }
      this.attachViewClass(Discourse.NotificationsButton);

      this.trigger('additionalButtons', this);
    } else {
      // If not logged in give them a login control
      this.attachViewClass(Discourse.LoginReplyButton);
    }
  }
});

/**
  This view is used for rendering the buttons at the footer of the topic

  @class TopicFooterButtonsView
  @extends Discourse.ContainerView
  @namespace Discourse
  @module Discourse
**/
Discourse.TopicFooterButtonsView = Discourse.ContainerView.extend({
  elementId: 'topic-footer-buttons',
  topicBinding: 'controller.content',

  init: function() {
    this._super();
    this.createButtons();
  },

  // Add the buttons below a topic
  createButtons: function() {
    var topic = this.get('topic');
    if (Discourse.User.current()) {
      if (!topic.get('isPrivateMessage')) {
        // We hide some controls from private messages
        if (this.get('topic.details.can_invite_to') && !this.get('topic.category.read_restricted')) {
          this.attachViewClass(Discourse.InviteReplyButton);
        }
        this.attachViewClass(Discourse.StarButton);
        this.attachViewClass(Discourse.ShareButton);
        this.attachViewClass(Discourse.ClearPinButton);
        if (this.get('topic.details.can_flag_topic')) {
          this.attachViewClass(Discourse.FlagTopicButton);
        }
      }
      if (this.get('topic.details.can_create_post')) {
        this.attachViewClass(Discourse.ReplyButton);
      }
      this.attachViewClass(Discourse.NotificationsButton);

      this.trigger('additionalButtons', this);
    } else {
      // If not logged in give them a login control
      this.attachViewClass(Discourse.LoginReplyButton);
    }
  }
});

Hide the Invite button in topics in secured categories


importScripts('https://www.gstatic.com/firebasejs/3.6.6/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.6.6/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
  // TODO add your messagingSenderId
  messagingSenderId: '662518903527'
});
var messaging = firebase.messaging();

importScripts('https://www.gstatic.com/firebasejs/3.6.6/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.6.6/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
  // TODO add your messagingSenderId
  messagingSenderId: ''
});
var messaging = firebase.messaging();

Remove hard-coded message sender id.


(function () {
	'use strict';
	// Awesome Browser Shortcuts
	chrome.commands.onCommand.addListener(function (command) {
		var tabQuery = {active: true, currentWindow: true};
		if (command === 'toggle-pin') {
			// Get current tab in the active window
			chrome.tabs.query(tabQuery, function(tabs) {
				var currentTab = tabs[0];
				chrome.tabs.update(currentTab.id, {'pinned': !currentTab.pinned});
			});
		}
		else if (command === 'move-tab-left') {
			chrome.tabs.query(tabQuery, function (tabs) {
				var currentTab = tabs[0];
				chrome.tabs.move(currentTab.id, {'index': currentTab.index - 1});
			});
		}
		else if (command === 'move-tab-right') {
			chrome.tabs.query(tabQuery, function (tabs) {
				var currentTab = tabs[0];
				// TODO: Move tab to first if current index is the last one
				// Chrome moves non-existent index to the last
				chrome.tabs.move(currentTab.id, {'index': currentTab.index + 1});
			});
		}
		else if (command === 'duplicate=tab') {
			chrome.tabs.query(tabQuery, function (tabs) {
				var currentTab = tabs[0];
				chrome.tabs.duplicate(currentTab.id);
			})
		}
	});
})();

(function () {
	'use strict';
	// Awesome Browser Shortcuts
	chrome.commands.onCommand.addListener(function (command) {
		var tabQuery = {active: true, currentWindow: true};
		if (command === 'toggle-pin') {
			// Get current tab in the active window
			chrome.tabs.query(tabQuery, function(tabs) {
				var currentTab = tabs[0];
				chrome.tabs.update(currentTab.id, {'pinned': !currentTab.pinned});
			});
		}
		else if (command === 'move-tab-left') {
			chrome.tabs.query(tabQuery, function (tabs) {
				var currentTab = tabs[0];
				chrome.tabs.move(currentTab.id, {'index': currentTab.index - 1});
			});
		}
		else if (command === 'move-tab-right') {
			chrome.tabs.query(tabQuery, function (tabs) {
				var currentTab = tabs[0];
				// TODO: Move tab to first if current index is the last one
				// Chrome moves non-existent index to the last
				chrome.tabs.move(currentTab.id, {'index': currentTab.index + 1});
			});
		}
		else if (command === 'duplicate-tab') {
			chrome.tabs.query(tabQuery, function (tabs) {
				var currentTab = tabs[0];
				chrome.tabs.duplicate(currentTab.id);
			});
		}
	});
})();

Fix Duplicate Tab shortcut typo


var fork = require('child_process').fork;
var amountConcurrentServers = process.argv[2] || 2;
var port = initialPortServer();
var servers = [];
var path = __dirname;
var disableAutomaticGarbageOption = '--expose-gc';

for(var i = 0; i < amountConcurrentServers; i++) {
    var portForServer = port + i;
    var serverIdentifier = i;
    console.log('Creating server: ' + serverIdentifier + ' - ' + portForServer + ' - ' + serverIdentifier);
    servers[i] = fork( path +'/server.js', [portForServer, serverIdentifier], {execArgv: [disableAutomaticGarbageOption]});
}

process.on('exit', function() {
    console.log('exit process');
    for(var i = 0; i < amountConcurrentServers; i++) {
        servers[i].kill();
    }
});

function initialPortServer() {
    if(process.argv[3]) {

        return parseInt(process.argv[3]);
    }

    return 3000;
}

var fork = require('child_process').fork;
var amountConcurrentServers = process.argv[2] || 2;
var port = initialPortServer();
var servers = [];
var path = __dirname;
var disableAutomaticGarbageOption = '--expose-gc';

for(var i = 0; i < amountConcurrentServers; i++) {
    var portForServer = port + i;
    var serverIdentifier = i;
    console.log('Creating server: ' + serverIdentifier + ' - ' + portForServer + ' - ' + serverIdentifier);
    servers[i] = fork( path +'/server.js', [portForServer, serverIdentifier], {execArgv: [disableAutomaticGarbageOption]});
    servers[i].on('exit', function() {
        console.log('[benchmark][app][' + i +'][event] EXIT');
    });
    servers[i].on('close', function() {
        console.log('[benchmark][app][' + i +'][event] EXIT');
    });
    servers[i].on('disconnect', function() {
        console.log('[benchmark][app][' + i +'][event] EXIT');
    });
    servers[i].on('error', function() {
        console.log('[benchmark][app][' + i +'][event] EXIT');
    });
    servers[i].on('uncaughtException', function() {
        console.log('[benchmark][app][' + i +'][event] EXIT');
    });
    servers[i].on('SIGTERM', function() {
        console.log('[benchmark][app][' + i +'][event] EXIT');
    });
    servers[i].on('SIGINT', function() {
        console.log('[benchmark][app][' + i +'][event] EXIT');
    });
}

process.on('exit', function() {
    console.log('exit process');
    for(var i = 0; i < amountConcurrentServers; i++) {
        servers[i].kill();
    }
});

function initialPortServer() {
    if(process.argv[3]) {

        return parseInt(process.argv[3]);
    }

    return 3000;
}

Add log for child process


Package.describe({
  name: 'staringatlights:autoform-generic-error',
  version: '1.0.0',
  // Brief, one-line summary of the package.
  summary: 'Enables generic error handling in AutoForm.',
  // URL to the Git repository containing the source code for this package.
  git: 'https://github.com/abecks/meteor-autoform-generic-error',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.versionsFrom('1.1.0.3');
  api.use(['templating', 'aldeed:autoform@5.3.0'], 'client');
  api.addFiles('autoform-generic-error.html', 'client');
  api.addFiles('autoform-generic-error.js', 'client');
});

Package.describe({
  name: 'staringatlights:autoform-generic-error',
  version: '1.0.0',
  // Brief, one-line summary of the package.
  summary: 'Enables generic error handling in AutoForm.',
  // URL to the Git repository containing the source code for this package.
  git: 'https://github.com/abecks/meteor-autoform-generic-error',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.versionsFrom('1.1.0.3');
  api.use(['templating', 'aldeed:autoform'], 'client');
  api.addFiles('autoform-generic-error.html', 'client');
  api.addFiles('autoform-generic-error.js', 'client');
});

Remove version constaint on AutoForm


module.exports = [
  {
    type: 'bitcoin',
    purpose: 'payment'
  },
  {
    type: 'bitcoin',
    purpose: 'messaging'
  },
  {
    type: 'ec',
    purpose: 'sign'
  },
  {
    type: 'ec',
    purpose: 'update'
  }
]

module.exports = [
  {
    type: 'bitcoin',
    purpose: 'payment'
  },
  {
    type: 'bitcoin',
    purpose: 'messaging'
  },
  {
    type: 'ec',
    purpose: 'sign'
  },
  {
    type: 'ec',
    purpose: 'update'
  },
  {
    type: 'dsa',
    purpose: 'sign'
  }
]

Revert "don't require dsa key"


import React from 'react'
import { StaticQuery, graphql } from 'gatsby'
import Navigation from '../components/navigation'

export default ({ children }) => (
  <StaticQuery
    query={graphql`
      query NavigationQuery {
        allMongodbPlacardDevSportsAndCountries {
          edges {
            node {
              name
              countries {
                name
              }
            }
          }
        }
      }
    `}
    render={data => (
      <div>
        <Navigation
          sports={data.allMongodbPlacardDevSportsAndCountries.edges}
        />
        {children}
      </div>
    )}
  />
)

import React from 'react'
import { StaticQuery, graphql } from 'gatsby'
import Navigation from '../components/navigation'

export default ({ children }) => (
  <StaticQuery
    query={graphql`
      query NavigationQuery {
        allMongodbPlacardDevSportsAndCountries {
          edges {
            node {
              name
              countries {
                name
              }
            }
          }
        }
      }
    `}
    render={data => (
      <>
        <Navigation
          sports={data.allMongodbPlacardDevSportsAndCountries.edges}
        />
        {children}
      </>
    )}
  />
)

Use short syntax version for the Fragment.


// Copyright 2014-2015, University of Colorado Boulder

/**
 * Base module for model elements whose position and opacity can change.
 *
 * @author John Blanco
 * @author Andrew Adare
 */
define( function( require ) {
  'use strict';

  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );

  /**
   * @param {PositionableFadableModelElement} modelElement
   * @param {ModelViewTransform} modelViewTransform
   * @constructor
   */
  function EFACBaseNode( modelElement, modelViewTransform ) {
    Node.call( this );

    /**
     * Update the overall offset based on the model position.
     *
     * @param  {Vector2} offset
     */
    modelElement.positionProperty.link( function( offset ) {
      this.setTranslation( modelViewTransform.ModelToViewPosition( offset ) );
    } );

    /**
     * Update the overall opacity base on model element opacity.
     *
     * @param  {Number} opacity
     */
    modelElement.opacityProperty.link( function( opacity ) {
      this.setOpacity( opacity );
    } );
  }

  return inherit( Node, EFACBaseNode );
} );

// Copyright 2014-2015, University of Colorado Boulder

/**
 * Base module for model elements whose position and opacity can change.
 *
 * @author John Blanco
 * @author Andrew Adare
 */
define( function( require ) {
  'use strict';

  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );

  /**
   * @param {PositionableFadableModelElement} modelElement
   * @param {ModelViewTransform} modelViewTransform
   * @constructor
   */
  function EFACBaseNode( modelElement, modelViewTransform ) {
    Node.call( this );

    var thisNode = this;

    /**
     * Update the overall offset based on the model position.
     *
     * @param  {Vector2} offset
     */
    modelElement.positionProperty.link( function( offset ) {
      thisNode.setTranslation( modelViewTransform.modelToViewPosition( offset ) );
    } );

    /**
     * Update the overall opacity base on model element opacity.
     *
     * @param  {Number} opacity
     */
    modelElement.opacityProperty.link( function( opacity ) {
      thisNode.setOpacity( opacity );
    } );
  }

  return inherit( Node, EFACBaseNode );
} );

Fix typo and use thisNode


var app = require('http').createServer();
var request = require('request');
var io = require('socket.io')(app);
var fs = require('fs');

app.listen(13374);

io.on('connection', function (socket) {

  setInterval(function() {
    request('http://status.hasi.it', function (error, response, body) {
      if (!error && response.statusCode == 200) {
        socket.emit('status', JSON.parse(body));
      }
    });
  }, 500)

});

var app = require('http').createServer();
var request = require('request');
var io = require('socket.io')(app);
var fs = require('fs');

app.listen(13374);

function pullData(socket) {
  request('http://status.hasi.it', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      socket.emit('status', JSON.parse(body));
    }
  });
}

io.on('connection', function (socket) {
  pullData(socket);
  setInterval(pullData, 5000, socket)
});

Add initial load and larger update interval


'use strict';

/**
 * @ngdoc overview
 * @name angularBootstrapCalendarApp
 * @description
 * # angularBootstrapCalendarApp
 *
 * Main module of the application.
 */
angular
  .module('mwl.calendar', [
    'ui.bootstrap.tooltip'
  ]);

'use strict';

/**
 * @ngdoc overview
 * @name angularBootstrapCalendarApp
 * @description
 * # angularBootstrapCalendarApp
 *
 * Main module of the application.
 */
angular
  .module('mwl.calendar', [
    'ui.bootstrap'
  ]);

Revert "Lower ui bootstraps dependency to only the tooltip"


define([
  'Class', 'uri', 'bluebird',
  'map/services/DataService'
], function(Class, UriTemplate, Promise, ds) {

  'use strict';

  var GET_REQUEST_ID = 'GeostoreService:get',
      SAVE_REQUEST_ID = 'GeostoreService:save';

  var URL = window.gfw.config.GFW_API_HOST + '/geostore/{id}';

  var GeostoreService = Class.extend({

    get: function(id) {
      return new Promise(function(resolve, reject) {

      var url = new UriTemplate(URL).fillFromObject({id: id});

      ds.define(GET_REQUEST_ID, {
        cache: {type: 'persist', duration: 1, unit: 'days'},
        url: url,
        type: 'GET'
      });

      var requestConfig = {
        resourceId: GET_REQUEST_ID,
        success: resolve
      };

      ds.request(requestConfig);

      });
    },

    save: function(geojson) {
      return new Promise(function(resolve, reject) {

      var url = new UriTemplate(URL).fillFromObject({});

      ds.define(SAVE_REQUEST_ID, {
        url: url,
        type: 'POST'
      });

      var params = { geojson: geojson };
      var requestConfig = {
        resourceId: SAVE_REQUEST_ID,
        data: JSON.stringify(params),
        success: function(response) {
          resolve(response.id);
        },
        error: reject
      };

      ds.request(requestConfig);

      });
    }

  });

  return new GeostoreService();

});

define([
  'Class', 'uri', 'bluebird',
  'map/services/DataService'
], function(Class, UriTemplate, Promise, ds) {

  'use strict';

  var GET_REQUEST_ID = 'GeostoreService:get',
      SAVE_REQUEST_ID = 'GeostoreService:save';

  var URL = window.gfw.config.GFW_API_HOST + '/geostore/{id}';

  var GeostoreService = Class.extend({

    get: function(id) {
      return new Promise(function(resolve, reject) {

      var url = new UriTemplate(URL).fillFromObject({id: id});

      ds.define(GET_REQUEST_ID, {
        cache: {type: 'persist', duration: 1, unit: 'days'},
        url: url,
        type: 'GET'
      });

      var requestConfig = {
        resourceId: GET_REQUEST_ID,
        success: resolve
      };

      ds.request(requestConfig);

      });
    },

    save: function(geojson) {
      return new Promise(function(resolve, reject) {

      var url = new UriTemplate(URL).fillFromObject({});

      ds.define(SAVE_REQUEST_ID, {
        url: url,
        type: 'POST'
      });

      var requestConfig = {
        resourceId: SAVE_REQUEST_ID,
        data: JSON.stringify(geojson),
        success: function(response) {
          resolve(response.id);
        },
        error: reject
      };

      ds.request(requestConfig);

      });
    }

  });

  return new GeostoreService();

});

Update to work with new Geostore API


/* global document, customElements, BaseElement, Moment */

class CalendarIllustration extends BaseElement {
    constructor() {
        super().fetchTemplate();
    }

    /**
     * Draws the illustration.
     *
     * @return {CalendarIllustration}
     */
    draw() {
        const moment = Moment();

        this.dataset.time = moment.format('ha');
        this.dataset.quarter = moment.format('Q');

        return this;
    }
}

// Remember document from import scope. Needed for accessing elements inside
// the imported html…
CalendarIllustration.ownerDocument = document.currentScript.ownerDocument;

// @see https://developer.mozilla.org/docs/Web/API/CustomElementRegistry/define
customElements.define('calendar-illustration', CalendarIllustration);

/* global document, customElements, BaseElement, Moment */

class CalendarIllustration extends BaseElement {
    constructor() {
        super().fetchTemplate();
    }

    /**
     * Draws the illustration.
     *
     * @return {CalendarIllustration}
     */
    draw() {
        const moment = Moment();

        this.dataset.time = moment.format('ha');
        this.dataset.month = moment.format('M');

        return this;
    }
}

// Remember document from import scope. Needed for accessing elements inside
// the imported html…
CalendarIllustration.ownerDocument = document.currentScript.ownerDocument;

// @see https://developer.mozilla.org/docs/Web/API/CustomElementRegistry/define
customElements.define('calendar-illustration', CalendarIllustration);

Drop quarter in favor of month


var stringUtil = require('ember-cli/lib/utilities/string');

module.exports = {
  description: 'Generates a new custom assertion into tests/assertions/',

  locals: function(options) {
    var entity    = options.entity;
    var rawName   = entity.name;
    var name      = stringUtil.dasherize(rawName);
    var camelName = stringUtil.camelize(rawName);

    return {
      name: name,
      camelName: camelName
    };
  }
}

var stringUtil = require('ember-cli-string-utils');

module.exports = {
  description: 'Generates a new custom assertion into tests/assertions/',

  locals: function(options) {
    var entity    = options.entity;
    var rawName   = entity.name;
    var name      = stringUtil.dasherize(rawName);
    var camelName = stringUtil.camelize(rawName);

    return {
      name: name,
      camelName: camelName
    };
  }
}

Update require statement for string utils


var iframe;

function initFrame() {
  iframe = document.createElement('iframe');
  document.body.appendChild(iframe);
}

// Listen to the parent window send src info to be set on the nested frame
function receiveNestedFrameData() {
  var handler = function(e) {
    if (e.source !== window.parent && !e.data.src) return;

    iframe.src = e.data.src;
    iframe.style.cssText = e.data.css;
    window.removeEventListener('message', handler);
  };

  window.addEventListener('message', handler);
}

// Listen to messages from nested frame and pass them up the window stack
function setupMessageRelay() {
  window.addEventListener('message', function(e) {
    var origin = e.origin || e.originalEvent.origin;
    if (origin !== 'https://buffer.com' || e.source !== iframe.contentWindow) {
      return;
    }

    window.parent.postMessage(e.data, '*');
  });
}

initFrame();
receiveNestedFrameData();
setupMessageRelay();

var iframe;

function initFrame() {
  iframe = document.createElement('iframe');
  document.body.appendChild(iframe);
}

// Listen to the parent window send src info to be set on the nested frame
function receiveNestedFrameData() {
  var handler = function(e) {
    if (e.source !== window.parent && !e.data.src) return;

    iframe.src = e.data.src;
    iframe.style.cssText = e.data.css;
    window.removeEventListener('message', handler);
  };

  window.addEventListener('message', handler);
}

// Listen to messages from nested frame and pass them up the window stack
function setupMessageRelay() {
  window.addEventListener('message', function(e) {
    var origin = e.origin || e.originalEvent.origin;
    if ((origin !== 'https://buffer.com' && origin !== 'https://local.buffer.com') || e.source !== iframe.contentWindow) {
      return;
    }

    window.parent.postMessage(e.data, '*');
  });
}

initFrame();
receiveNestedFrameData();
setupMessageRelay();

Make extension work in dev env too


import React, { Component } from 'react';
import { Container, Tab, Tabs, Text } from 'native-base';
import WorkerInfoScreen from '../worker-info-screen';
import I18n from '../../../i18n';

// @TODO Replace temporary data with data from Redux/API
const NAME = 'John Doe';

export default class WorkerProfileScreen extends Component {
  static navigationOptions = {
    title: I18n.t('screen_titles.worker_profile', { name: NAME }),
  };

  // TODO Replace reference placeholder text with reference screen.
  render() {
    return (
      <Container>
        <Tabs>
          <Tab heading={I18n.t('account.profile_info')} >
            <WorkerInfoScreen />
          </Tab>
          <Tab heading={I18n.t('account.references')}>
            <Text>References screen goes here</Text>
          </Tab>
        </Tabs>
      </Container>
    );
  }
}

import React, { Component } from 'react';
import { Container, Tab, Tabs } from 'native-base';
import WorkerInfoScreen from '../worker-info-screen';
import ReferenceScreen from '../reference-screen';
import I18n from '../../../i18n';

// @TODO Replace temporary data with data from Redux/API
const NAME = 'John Doe';

export default class WorkerProfileScreen extends Component {
  static navigationOptions = {
    title: I18n.t('screen_titles.worker_profile', { name: NAME }),
  };

  // TODO Replace placeholder data in WorkerInfoScreen and ReferenceScreen
  render() {
    return (
      <Container>
        <Tabs>
          <Tab heading={I18n.t('account.profile_info')} >
            <WorkerInfoScreen />
          </Tab>
          <Tab heading={I18n.t('account.references')}>
            <ReferenceScreen />
          </Tab>
        </Tabs>
      </Container>
    );
  }
}

Add ReferenceScreen to references tab in WorkerProfileScreen


import AbstractModelService from './abstract-model-service'
import { axiosGet } from '@/helpers/axios'

/**
 * API service for Publishers.
 */
class PublisherService extends AbstractModelService {
	constructor() {
		super('publishers/', {
			fillMissingObjects: ['logo'],
			sanitizeHtml: ['history']
		})
	}

	/**
	 * Finds how many Albums use the given Publisher.
	 * @param {Number} id The Publisher ID
	 */
	countAlbums(id) {
		return axiosGet(`publishers/${id}/albums/count`, 0)
	}
}

export default new PublisherService()

import AbstractModelService from './abstract-model-service'
import { axiosGet } from '@/helpers/axios'

/**
 * API service for Publishers.
 */
class PublisherService extends AbstractModelService {
	constructor() {
		super('publishers/', {
			fillMissingObjects: ['logo'],
			sanitizeHtml: ['history']
		})
	}

	/**
	 * Finds the Collections belonging to a Publisher.
	 * @param {Number} publisherId The Publisher ID
	 */
	findCollectionsForList(publisherId) {
		return axiosGet(`${this.basePath}${publisherId}/collections`, [])
	}

	/**
	 * Finds how many Albums use the given Publisher.
	 * @param {Number} id The Publisher ID
	 */
	countAlbums(id) {
		return axiosGet(`publishers/${id}/albums/count`, 0)
	}
}

export default new PublisherService()

Create a method to search for a Publisher's Collections.


// Add Etag header to each http and rpc response
//

'use strict';

const crypto = require('crypto');

module.exports = function (N) {
  N.wire.after([ 'responder:http', 'responder:rpc' ], { priority: 95 }, function etag_304_revalidate(env) {
    if (env.status !== 200) return;
    if (!env.body || typeof env.body !== 'string') return;
    if (env.headers['ETag'] || env.headers['Cache-Control']) return;

    let etag = '"' + crypto.createHash('sha1').update(env.body).digest('base64').substring(0, 27) + '"';

    env.headers['ETag'] = etag;
    env.headers['Cache-Control'] = 'must-revalidate';

    if (etag === env.origin.req.headers['if-none-match']) {
      env.status = N.io.NOT_MODIFIED;
      env.body = null;
    }
  });
};

// Add Etags, 304 responses, and force revalidate for each request
//
// - Should help with nasty cases, when quick page open use old
//   assets and show errors until Ctrl+F5
// - Still good enougth for user, because 304 responses supported
//
'use strict';

const crypto = require('crypto');

module.exports = function (N) {
  N.wire.after([ 'responder:http', 'responder:rpc' ], { priority: 95 }, function etag_304_revalidate(env) {

    // Quick check if we can intrude
    if (env.status !== 200) return;
    if (!env.body) return;
    if (typeof env.body !== 'string' && !Buffer.isBuffer(env.body)) return;
    if (env.headers['ETag'] || env.headers['Cache-Control']) return;

    // Fill Etag/Cache-Control headers
    let etag = '"' + crypto.createHash('sha1').update(env.body).digest('base64').substring(0, 27) + '"';

    env.headers['ETag'] = etag;
    env.headers['Cache-Control'] = 'max-age=0, must-revalidate';

    // Replace responce status if possible
    if (etag === env.origin.req.headers['if-none-match']) {
      env.status = N.io.NOT_MODIFIED;
      env.body = null;
    }
  });
};

Improve cache headers and add comments


import './Menu.css';
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { closeMenu } from 'shared/state/app/actions';
import shallowCompare from 'react-addons-shallow-compare';

class Menu extends Component {
    constructor(props) {
        super(props);
        this._handleOutsideClick = this._handleOutsideClick.bind(this);
    }

    componentDidMount() {
        document.body.addEventListener('click', this._handleOutsideClick);
    }

    shouldComponentUpdate(nextProps, nextState) {
        return shallowCompare(this, nextProps, nextState);
    }

    componentWillUnmount() {
        document.body.removeEventListener('click', this._handleOutsideClick);
    }

    render() {
        return (
            <div
                className={ `menu-component ${this.props.isOpen ? 'is-open' : ''}` }
                ref={ (ref) => { this._el = ref; } }>
            </div>
        );
    }

    _handleOutsideClick(e) {
        !this._el.contains(e.target) && this.props.dispatch(closeMenu());
    }
}

Menu.propTypes = {
    isOpen: PropTypes.bool.isRequired,
    dispatch: PropTypes.func.isRequired,
};

export default connect((state) => ({
    isOpen: state.app.isMenuOpen,
}))(Menu);

import './Menu.css';
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { closeMenu } from 'shared/state/app/actions';
import shallowCompare from 'react-addons-shallow-compare';

class Menu extends Component {
    constructor(props) {
        super(props);
        this._handleOutsideClickOrTap = this._handleOutsideClickOrTap.bind(this);
    }

    componentDidMount() {
        document.body.addEventListener('click', this._handleOutsideClickOrTap);
        document.body.addEventListener('touchend', this._handleOutsideClickOrTap);
    }

    shouldComponentUpdate(nextProps, nextState) {
        return shallowCompare(this, nextProps, nextState);
    }

    componentWillUnmount() {
        document.body.removeEventListener('click', this._handleOutsideClickOrTap);
        document.body.removeEventListener('touchend', this._handleOutsideClickOrTap);
    }

    render() {
        return (
            <div
                className={ `menu-component ${this.props.isOpen ? 'is-open' : ''}` }
                ref={ (ref) => { this._el = ref; } }>
            </div>
        );
    }

    _handleOutsideClickOrTap(e) {
        if (this.props.isOpen && !this._el.contains(e.target)) {
            this.props.dispatch(closeMenu());
        }
    }
}

Menu.propTypes = {
    isOpen: PropTypes.bool.isRequired,
    dispatch: PropTypes.func.isRequired,
};

export default connect((state) => ({
    isOpen: state.app.isMenuOpen,
}))(Menu);

Handle taps to close the menu.


import browserSync from 'browser-sync';
import paths from '../mconfig.json';

export const server = browserSync.create();

function serve(done) {
    server.init({
        notify: false,
        proxy: paths.url,
        host: paths.url,
        open: 'external'
    });
    done();
}

export default serve;

import browserSync from 'browser-sync';
import paths from '../mconfig.json';

export const server = browserSync.create();

function serve(done) {
    server.init({
        notify: false,
        proxy: paths.url,
        host: paths.url,
        open: false,
        ghostMode: false
    });
    done();
}

export default serve;

Remove browser-sync new window & ghostMode


const createAppTemplate = (app, template) => {
  const hadClassName = !!app.className;
  const quenchedClassName = `${app.className} quenched`.replace(/\bpre-quench\b/g, '');

  let html = app.outerHTML
    .replace(/<!--\s*<q>\s*-->[\s\S]*?<!--\s*<\/q>\s*-->/g, '')
    .replace(/<!--\s*q-binding:.*?-->/g, '')
    .replace(/\sq-binding=".*?\s+as\s+/g, ' q-binding="')
    .replace(/q-binding="/g, 'v-text="')
    .replace(/(\sv-(text|html).*?>)[\s\S]*?<\//g, '$1</')
    .replace(/(?:<!--\s*)?<q-template><\/q-template>(\s*-->)?/, template || '');

  if (hadClassName) {
    html = html.replace(/(class=")[\s\S]*?"/, `$1${quenchedClassName}"`);
  }
  else {
    html = html.replace(/(<.*?\s)/, `$1class="${quenchedClassName}"`);
  }

  return html;
};

export default createAppTemplate;

const createAppTemplate = (app, template) => {
  const quenchedClassName = `${app.className} quenched`.replace(/\bpre-quench\b/g, '');
  const html = app.outerHTML
    .replace(/<!--\s*<q>\s*-->[\s\S]*?<!--\s*<\/q>\s*-->/g, '')
    .replace(/<!--\s*q-binding:.*?-->/g, '')
    .replace(/\sq-binding=".*?\s+as\s+/g, ' q-binding="')
    .replace(/q-binding="/g, 'v-text="')
    .replace(/(\sv-(text|html).*?>)[\s\S]*?<\//g, '$1</')
    .replace(/(?:<!--\s*)?<q-template><\/q-template>(\s*-->)?/, template || '');

  if (app.hasAttribute('class')) {
    return html.replace(/(class=")[\s\S]*?"/, `$1${quenchedClassName}"`);
  }

  return html.replace(/(<.*?\s)/, `$1class="${quenchedClassName}"`);
};

export default createAppTemplate;

Fix apps with empty class attributes


const billion = 1000000000.0
const million = 1000000.0
const thousand = 1000.0

export function numberToHuman(number, showZero = true) {
  if (number === 0 && !showZero) { return '' }
  let num
  let suffix
  if (number >= billion) {
    num = Math.round((number / billion) * 100.0) / 100.0
    suffix = 'B'
  } else if (number >= million) {
    num = Math.round((number / million) * 100.0) / 100.0
    suffix = 'M'
  } else if (number >= thousand) {
    num = Math.round((number / thousand) * 100.0) / 100.0
    suffix = 'K'
  } else {
    num = Math.round(number * 100.0) / 100.0
    suffix = ''
  }
  let strNum = `${num}`
  const strArr = strNum.split('.')
  if (strArr[strArr.length - 1] === '0') {
    strNum = strArr[0]
  }
  return `${strNum}${suffix}`
}

export default numberToHuman

const billion = 1000000000.0
const million = 1000000.0
const thousand = 1000.0

export function numberToHuman(number, showZero = true, precision = 1) {
  if (number === 0 && !showZero) { return '' }
  const roundingFactor = 10 ** precision
  let num
  let suffix
  if (number >= billion) {
    num = Math.round((number / billion) * roundingFactor) / roundingFactor
    suffix = 'B'
  } else if (number >= million) {
    num = Math.round((number / million) * roundingFactor) / roundingFactor
    suffix = 'M'
  } else if (number >= thousand) {
    num = Math.round((number / thousand) * roundingFactor) / roundingFactor
    suffix = 'K'
  } else {
    num = Math.round(number * roundingFactor) / roundingFactor
    suffix = ''
  }
  let strNum = `${num}`
  const strArr = strNum.split('.')
  if (strArr[strArr.length - 1] === '0') {
    strNum = strArr[0]
  }
  return `${strNum}${suffix}`
}

export default numberToHuman

Change the rounding precision to one decimal point


module.exports = {
  "env": {
    "browser": true
  },
  "extends": [
    "airbnb",
    // These prettier configs are used to disable inherited rules that conflict
    // with the way prettier will format code. Full info here:
    // https://github.com/prettier/eslint-config-prettier
    "prettier",
    "prettier/flowtype",
    "prettier/react"
  ],
  "parser": "babel-eslint",
  "plugins": [
    "flowtype",
    "import",
    "react",
    "prettier"
  ],
  "rules": {
    "flowtype/define-flow-type": "error",
    "flowtype/require-valid-file-annotation": [
      2,
      "always"
    ],
    "flowtype/type-id-match": [
      "error",
      "^([A-Z][a-z0-9]+)+$"
    ],
    "flowtype/use-flow-type": "error",
    "import/no-duplicates": "error",
    "no-duplicate-imports": "off",
    "no-nested-ternary": "off",
    "no-warning-comments": [
      "error",
      {
        "terms": [
          "fixme"
        ]
      }
    ],
    "react/no-unused-prop-types": "off",
    "prettier/prettier": [
      "error",
      {
        // Options to pass to prettier: https://github.com/prettier/prettier#api
        "singleQuote": true,
        "trailingComma": "all"
      },
      "@prettier"
    ]
  }
};

module.exports = {
  "env": {
    "browser": true
  },
  "extends": [
    "airbnb",
    // These prettier configs are used to disable inherited rules that conflict
    // with the way prettier will format code. Full info here:
    // https://github.com/prettier/eslint-config-prettier
    "prettier",
    "prettier/flowtype",
    "prettier/react"
  ],
  "parser": "babel-eslint",
  "plugins": [
    "flowtype",
    "import",
    "react",
    "prettier"
  ],
  "rules": {
    "flowtype/define-flow-type": "error",
    "flowtype/require-valid-file-annotation": [
      2,
      "always"
    ],
    "flowtype/type-id-match": [
      "error",
      "^([A-Z][a-z0-9]+)+$"
    ],
    "flowtype/use-flow-type": "error",
    "import/no-duplicates": "error",
    "import/no-extraneous-dependencies": [
      "error",
      { "devDependencies": ["**/test.jsx", "**/demo.jsx",  "**/*.demo.jsx", "**/demo/*.jsx"] }
    ],
    "no-duplicate-imports": "off",
    "no-nested-ternary": "off",
    "no-warning-comments": [
      "error",
      {
        "terms": [
          "fixme"
        ]
      }
    ],
    "react/no-unused-prop-types": "off",
    "prettier/prettier": [
      "error",
      {
        // Options to pass to prettier: https://github.com/prettier/prettier#api
        "singleQuote": true,
        "trailingComma": "all"
      },
      "@prettier"
    ]
  }
};

Allow devDependencies in demos and tests


function handleVueDestruction(vue) {
  document.addEventListener('turbolinks:before-render', function teardown() {
    vue.$destroy();
    document.removeEventListener('turbolinks:before-render', teardown);
  });
}

var TurbolinksAdapter = {
  beforeMount: function() {
    if (this.$el.parentNode) {
      handleVueDestruction(this);
      this.$originalEl = this.$el.outerHTML;
    }
  },
  destroyed: function() {
    this.$el.outerHTML = this.$originalEl;
  }
};

export default TurbolinksAdapter;

function handleVueDestruction(vue) {
  document.addEventListener('turbolinks:visit', function teardown() {
    vue.$destroy();
    document.removeEventListener('turbolinks:visit', teardown);
  });
}

var TurbolinksAdapter = {
  beforeMount: function() {
    if (this.$el.parentNode) {
      handleVueDestruction(this);
      this.$originalEl = this.$el.outerHTML;
    }
  },
  destroyed: function() {
    this.$el.outerHTML = this.$originalEl;
  }
};

export default TurbolinksAdapter;

Use turbolinks:visit event to handle teardowns, works whether caching is enabled or not


define(function() {
    'use strict';

    /**
     * @param {string} str
     * @return {string}
     */
    var capitalize = function(str) {
        return str.substr(0, 1).toUpperCase() + str.substr(1);
    };

    return { capitalize };
});

define(function() {
    'use strict';

    /**
     * @param {string} str
     * @return {string}
     */
    let capitalize = function(str) {
        return str.substr(0, 1).toUpperCase() + str.substr(1);
    };

    return { capitalize };
});

Use `let` instead of `var`


// @flow
import type { MessageThemeContext } from './types'
import React, { PureComponent } from 'react'
import styled from '../styled'
import Chat from './Chat'
import classNames from '../../utilities/classNames'
import css from './styles/Embed.css.js'

type Props = {
  className?: string,
  html: string,
}

type Context = MessageThemeContext

class Embed extends PureComponent<Props, Context> {
  static displayName = 'Message.Embed'

  render() {
    const { className, html, ...rest } = this.props
    const { theme } = this.context

    const componentClassName = classNames(
      'c-MessageEmbed',
      theme && `is-theme-${theme}`,
      className
    )

    return (
      <Chat
        {...rest}
        bubbleClassName="c-MessageEmbed__bubble"
        className={componentClassName}
      >
        <div
          dangerouslySetInnerHTML={{ __html: html }}
          className="c-MessageEmbed__html"
        />
      </Chat>
    )
  }
}

export default styled(Embed)(css)

// @flow
import type { MessageThemeContext } from './types'
import React, { PureComponent } from 'react'
import styled from '../styled'
import Chat from './Chat'
import classNames from '../../utilities/classNames'
import css from './styles/Embed.css.js'

type Props = {
  className?: string,
  html: string,
}

type Context = MessageThemeContext

class Embed extends PureComponent<Props, Context> {
  static displayName = 'Message.Embed'

  render() {
    const { className, html, ...rest } = this.props
    const { theme } = this.context

    const componentClassName = classNames(
      'c-MessageEmbed',
      /* istanbul ignore next */
      // Tested, but Istanbul isn't picking it up.
      theme && `is-theme-${theme}`,
      className
    )

    return (
      <Chat
        {...rest}
        bubbleClassName="c-MessageEmbed__bubble"
        className={componentClassName}
      >
        <div
          dangerouslySetInnerHTML={{ __html: html }}
          className="c-MessageEmbed__html"
        />
      </Chat>
    )
  }
}

export default styled(Embed)(css)

Add istanbul ignore. Line is tested.


const Configstore = require('configstore')
const inq = require('inquirer')
const prompts = require('./setup-prompts')

module.exports = class ServerConfig {

  constructor(defaults) {
    this.db = new Configstore('wowser', defaults)
  }

  initSetup() {
    return new Promise((resolve, reject) => {
      console.log('> Preparing initial setup\n');

      this.db.set('isFirstRun', false);

      inq.prompt(prompts, answers => {
        Object.keys(answers).map(key => {
          return this.db.set(key, answers[key]);
        });

        resolve('\n> Setup finished!\n');
      });
    })
  }
}

const Configstore = require('configstore')
const inq = require('inquirer')
const pkg = require('../../../../package.json')
const prompts = require('./setup-prompts')

module.exports = class ServerConfig {

  constructor(defaults) {
    this.db = new Configstore(pkg.name, defaults)
  }

  initSetup() {
    return new Promise((resolve, reject) => {
      console.log('> Preparing initial setup\n');

      this.db.set('isFirstRun', false);

      inq.prompt(prompts, answers => {
        Object.keys(answers).map(key => {
          return this.db.set(key, answers[key]);
        });

        resolve('\n> Setup finished!\n');
      });
    })
  }
}

Use package name from package.json as a configstore id


var content = (function() {
  return {
    normalizeTags: function(element) {
      var i, j, node, sibling;

      var fragment = document.createDocumentFragment();

      for (i = 0; i < element.childNodes.length; i++) {
        node = element.childNodes[i];
        if(!node) continue;

        // skip empty tags, so they'll get removed
        if(node.nodeName !== 'BR' && !node.textContent) continue;

        if(node.nodeType === 1 && node.nodeName !== 'BR') {
          sibling = node;
          while((sibling = sibling.nextSibling) !== null) {
            if(!parser.isSameNode(sibling, node))
              break;

            for(j = 0; j < sibling.childNodes.length; j++) {
              node.appendChild(sibling.childNodes[j].cloneNode(true));
            }

            sibling.parentNode.removeChild(sibling);
          }

          this.normalizeTags(node);
        }

        fragment.appendChild(node.cloneNode(true));
      }

      while (element.firstChild) {
        element.removeChild(element.firstChild);
      }
      element.appendChild(fragment);
    },

    cleanInternals: function(element) {
      element.innerHTML = element.innerHTML.replace(/\u200B/g, '<br />');
    },

    normalizeSpaces: function(element) {
      var firstChild = element.firstChild;

      if(!firstChild) return;

      if(firstChild.nodeType === 3) {
        firstChild.nodeValue = firstChild.nodeValue.replace(/^(\s)/, '\u00A0');
      }
      else {
        this.normalizeSpaces(firstChild);
      }
    }
  };
})();

var content = (function() {
  return {
    normalizeTags: function(element) {
      var i, j, node, sibling;

      var fragment = document.createDocumentFragment();

      for (i = 0; i < element.childNodes.length; i++) {
        node = element.childNodes[i];
        if(!node) continue;

        // skip empty tags, so they'll get removed
        if(node.nodeName !== 'BR' && !node.textContent) continue;

        if(node.nodeType === 1 && node.nodeName !== 'BR') {
          sibling = node;
          while((sibling = sibling.nextSibling) !== null) {
            if(!parser.isSameNode(sibling, node))
              break;

            for(j = 0; j < sibling.childNodes.length; j++) {
              node.appendChild(sibling.childNodes[j].cloneNode(true));
            }

            sibling.parentNode.removeChild(sibling);
          }

          this.normalizeTags(node);
        }

        fragment.appendChild(node.cloneNode(true));
      }

      while (element.firstChild) {
        element.removeChild(element.firstChild);
      }
      element.appendChild(fragment);
    },

    cleanInternals: function(element) {
      element.innerHTML = element.innerHTML.replace(/\u200B/g, '<br />');
    },

    normalizeSpaces: function(element) {
      if(!element) return;

      if(element.nodeType === 3) {
        element.nodeValue = element.nodeValue.replace(/^(\s)/, '\u00A0').replace(/(\s)$/, '\u00A0');
      }
      else {
        this.normalizeSpaces(element.firstChild);
        this.normalizeSpaces(element.lastChild);
      }
    }
  };
})();

Normalize trailing spaces as well


'use babel'

import Path from 'path'
import {getDir, findFile} from './common'
import {CONFIG_FILE_NAME} from '../defaults'

export function findRoot(path, options) {
  if (options.root !== null) {
    return options.root
  }

  const searchPath = getDir(path.indexOf('*') === -1 ? path : options.cwd)
  const configFile = findFile(searchPath, CONFIG_FILE_NAME)

  if (configFile === null) {
    return searchPath
  } else {
    return Path.dirname(configFile)
  }
}

'use babel'

import Path from 'path'
import {getDir, findFile} from './common'
import {CONFIG_FILE_NAME} from '../defaults'
import isGlob from 'is-glob'

export function findRoot(path, options) {
  if (options.root !== null) {
    return options.root
  }

  const searchPath = getDir(isGlob(path) ? options.cwd : path)
  const configFile = findFile(searchPath, CONFIG_FILE_NAME)

  if (configFile === null) {
    return searchPath
  } else {
    return Path.dirname(configFile)
  }
}

Use is-glob when finding root


const moment = require('moment-timezone');
const chalk = require('chalk');

exports.printMessage = function (message) {
  console.log(message);
};

exports.printError = function (message) {
  const errorOutput = chalk`{bgRed Error} ${message}`;

  console.log(errorOutput);
};

exports.convertTimezone = function (time, timezone, callback) {
  if (moment.tz.zone(timezone)) {
    const newTime = moment.tz(new Date(time), timezone).format('MMMM D, YYYY HH:mm:ss z');
    return callback(null, newTime);
  }
  return callback('Unrecognised time zone.');
};

const moment = require('moment-timezone');
const chalk = require('chalk');

exports.printMessage = function (message) {
  console.log(message);
};

exports.printError = function (message) {
  const errorOutput = chalk`{bgRed Error} ${message}`;

  console.log(errorOutput);
};

exports.isValidTimezone = function (timezone) {
  return moment.tz.zone(timezone);
};

exports.convertTimezone = function (time, timezone, callback) {
  if (moment.tz.zone(timezone)) {
    const newTime = moment.tz(new Date(time), timezone).format('MMMM D, YYYY HH:mm:ss z');
    return callback(null, newTime);
  }
  return callback('Unrecognised time zone.');
};

Add helper with timezone validity info


require('./dist/dialogs.js');
require('./dist/dialogs-default-translations.js');
require('./dist/dialogs.css');
module.exports = 'dialogs.main';

require('./dist/dialogs.js');
require('./dist/dialogs-default-translations.js');
module.exports = 'dialogs.main';

Fix problems with importing file


module.exports = function (opts={}) {
  var isJsonFile = opts.filter || /\.json$/;

  return function (override, transform, control) {
    transform("readSource", function (source) {
      const asset = this.args[0];
      if (isJsonFile.test(asset.path)) {
        source = `module.exports = ${source};`;
      }
      return source;
    });
  };
};

module.exports = function (opts={}) {
  var isJsonFile = opts.filter || /\.json$/;

  return function (override, transform, control) {
    transform("readSource", function (source, args) {
      const asset = args[0];
      if (isJsonFile.test(asset.path)) {
        source = `module.exports = ${source};`;
      }
      return source;
    });
  };
};

Update transform to accomodate more explicit transform API changes.


var originalDataModule = require('data-module');
var gutil = require('gulp-util');
var through2 = require('through2');

var DataModuleError = gutil.PluginError.bind(null, 'gulp-data-module');

var dataModule = function dataModule (options) { 'use strict';
    if (!options) options = {};
    var parsing = options.parsing || JSON.parse;

    var dataModuleOptions = {};
    if (typeof options.formatting == 'function') {
        dataModuleOptions.formatting = options.formatting;
        }

    return through2.obj(function dataModuleStream (file, encoding, done) {
        var source;

        if (file.isBuffer()) {
            source = parsing(file.contents.toString());

            file.contents = originalDataModule
                ( source
                , dataModuleOptions
                ).toBuffer();
            file.path = file.path.replace(/(?:\.[^\/\\\.]$|$)/, '.js');
            }

        else if (file.isStream()) return done(new DataModuleError
            ( 'Streams not supported'
            ));

        this.push(file);
        return done();
        });
    };

dataModule.formatting =
    { diffy: dataModule.diffy
    };

module.exports = dataModule;

var _dataModule = require('data-module');
var gutil = require('gulp-util');
var stream = require('through2').obj;

var DataModuleError = gutil.PluginError.bind(null, 'gulp-data-module');

var dataModule = function dataModule (options) { 'use strict';
    if (!options) options = {};
    var parsing = options.parsing || JSON.parse;

    var dataModuleOptions = {};
    if (typeof options.formatting == 'function') {
        dataModuleOptions.formatting = options.formatting;
        }

    return stream(function dataModuleStream (file, encoding, done) {
        var source;

        if (file.isBuffer()) {
            source = parsing(file.contents.toString());

            file.contents = _dataModule
                ( source
                , dataModuleOptions
                ).toBuffer();
            file.path = gutil.replaceExtension('.js');
            }

        else if (file.isStream()) return done(new DataModuleError
            ( 'Streams not supported'
            ));

        this.push(file);
        return done();
        });
    };

dataModule.formatting = _dataModule.formatting;

module.exports = dataModule;

Update from wherever 0.0.3 came from


/* jshint node: true */
'use strict';

const REFLECT_JS_VERSION = '0.1.59',
      REFLECT_JAVASCRIPT = 'https://cdn.reflect.io/' + REFLECT_JS_VERSION +'/reflect.js',
      REFLECT_CSS = 'https://cdn.reflect.io/' + REFLECT_JS_VERSION + '/reflect.css';

const HEAD = '<script src="' + REFLECT_JAVASCRIPT + '" type="text/javascript"></script> \
<link rel="stylesheet" href="' + REFLECT_CSS + '">';

module.exports = {
  name: 'ember-cli-reflect',
  contentFor: function(type) {
    if (type === 'head') { return HEAD };
  }
};

/* jshint node: true */
'use strict';

const REFLECT_JS_VERSION = '0.1.59',
      REFLECT_JAVASCRIPT = 'https://cdn.reflect.io/' + REFLECT_JS_VERSION +'/reflect.js';

const getHead = function(config) {
  let cssSource;

  if (config.reflect && config.reflect.css) {
    cssSource = config.reflect.css;
  } else {
    cssSource = 'https://cdn.reflect.io/' + REFLECT_JS_VERSION + '/reflect.css';
  }

  return '<script src="' + REFLECT_JAVASCRIPT + '" type="text/javascript"></script> \
<link rel="stylesheet" href="' + cssSource + '">';
};

module.exports = {
  name: 'ember-cli-reflect',
  contentFor: function(type) {
    if (type === 'head') {
      let config = this.config();

      return getHead(config);
    };
  }
};

Allow user to define their own css source


'use strict'
const app = require('app')
const BrowserWindow = require('browser-window')
const Menu = require('menu')
const menuTemplate = require('./menu-template.js')

require('electron-debug')()

let mainWindow = null

function onClosed () {
  mainWindow = null
}

function createMainWindow () {
  const win = new BrowserWindow({
    width: 920,
    height: 620,
    center: true,
    'standard-window': false,
    frame: false,
    title: 'Blox Party'
  })
  win.loadUrl('file://' + __dirname + '/index.html')
  win.on('closed', onClosed)
  return win
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

app.on('activate-with-no-open-windows', () => {
  if (!mainWindow) mainWindow = createMainWindow()
})

app.on('ready', () => {
  // Build menu only for OSX
  if (Menu.sendActionToFirstResponder) {
    Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplate))
  }
  mainWindow = createMainWindow()
})

'use strict'
const app = require('app')
const BrowserWindow = require('browser-window')
const ipc = require('ipc')
const Menu = require('menu')
const menuTemplate = require('./menu-template.js')

require('electron-debug')()

let mainWindow = null

function onClosed () {
  mainWindow = null
}

function createMainWindow () {
  const win = new BrowserWindow({
    width: 920,
    height: 620,
    center: true,
    'standard-window': false,
    frame: false,
    title: 'Blox Party'
  })
  win.loadUrl('file://' + __dirname + '/index.html')
  win.on('closed', onClosed)
  return win
}

ipc.on('quit', function () {
  app.quit()
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

app.on('activate-with-no-open-windows', () => {
  if (!mainWindow) mainWindow = createMainWindow()
})

app.on('ready', () => {
  // Build menu only for OSX
  if (Menu.sendActionToFirstResponder) {
    Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplate))
  }
  mainWindow = createMainWindow()
})

Use ipc module. Check for existence of Electron.


'use strict';

var
	platform = require('enyo/platform'),
	dispatcher = require('enyo/dispatcher'),
	gesture = require('enyo/gesture');

exports = module.exports = require('./lib/options');
exports.version = '2.6.0-pre';

// Override the default holdpulse config to account for greater delays between keydown and keyup
// events in Moonstone with certain input devices.
gesture.drag.configureHoldPulse({
	frequency: 200,
	events: [{name: 'hold', time: 400}],
	resume: false,
	moveTolerance: 16,
	endHold: 'onMove'
});

/**
* Registers key mappings for webOS-specific device keys related to media control.
*
* @private
*/
if (platform.webos >= 4) {
	// Table of default keyCode mappings for webOS device
	dispatcher.registerKeyMap({
		415 : 'play',
		413 : 'stop',
		19  : 'pause',
		412 : 'rewind',
		417 : 'fastforward',
		461 : 'back'
	});
}

// ensure that these are registered
require('./lib/resolution');
require('./lib/fonts');

'use strict';

var
	platform = require('enyo/platform'),
	dispatcher = require('enyo/dispatcher'),
	gesture = require('enyo/gesture');

exports = module.exports = require('./lib/options');
exports.version = '2.6.0-pre';

// Override the default holdpulse config to account for greater delays between keydown and keyup
// events in Moonstone with certain input devices.
gesture.drag.configureHoldPulse({
	frequency: 200,
	events: [{name: 'hold', time: 400}],
	resume: false,
	moveTolerance: 1500,
	endHold: 'onMove'
});

/**
* Registers key mappings for webOS-specific device keys related to media control.
*
* @private
*/
if (platform.webos >= 4) {
	// Table of default keyCode mappings for webOS device
	dispatcher.registerKeyMap({
		415 : 'play',
		413 : 'stop',
		19  : 'pause',
		412 : 'rewind',
		417 : 'fastforward',
		461 : 'back'
	});
}

// ensure that these are registered
require('./lib/resolution');
require('./lib/fonts');

Increase moveTolerance value from 16 to 1500 on configureHoldPulse


define(function() {
    var ProjectsFeaturedController = function(ProjectResource) {
        this.projects = ProjectResource.list({
            // Add feature tags here
            'tags[]': []
        })
    }

    ProjectsFeaturedController.$inject = ['ProjectResource']

    return ProjectsFeaturedController
})

define(function() {
    var ProjectsFeaturedController = function(ProjectResource) {
        this.projects = ProjectResource.list({
            // Add feature tags here
            'tags[]': ['noit', 'explore']
        })
    }

    ProjectsFeaturedController.$inject = ['ProjectResource']

    return ProjectsFeaturedController
})

Edit tags in featured projects controller


import R from 'ramda';
import compose from './util/compose.js';
import join from './util/join.js';
import upperCase from './util/upper-case.js';
import lowerCase from './util/lower-case.js';
import isUpperCase from '../src/is-upper-case.js';

// a -> a
const swapCase = compose(
  join(''),
  R.map(
    R.either(
      R.both(isUpperCase, lowerCase),
      R.both(compose(R.not, isUpperCase), upperCase)
    )
  )
);

export default swapCase;

import R from 'ramda';
import compose from './util/compose.js';
import join from './util/join.js';
import not from './util/not.js';
import upperCase from './util/upper-case.js';
import lowerCase from './util/lower-case.js';
import isUpperCase from '../src/is-upper-case.js';

// a -> a
const swapCase = compose(
  join(''),
  R.map(
    R.either(
      R.both(isUpperCase, lowerCase),
      R.both(compose(not, isUpperCase), upperCase)
    )
  )
);

export default swapCase;

Refactor swapCase function to use custom not function


// Lifted from Atom

exports.beforeEach = function (fn) {
  global.beforeEach(function () {
    var result = fn()
    if (result instanceof Promise) {
      waitsForPromise(function () { return result })
    }
  })
}

exports.afterEach = function (fn) {
  global.afterEach(function () {
    var result = fn()
    if (result instanceof Promise) {
      waitsForPromise(function () { return result })
    }
  })
}

var matchers = ['it', 'fit', 'ffit', 'fffit'] // inlining this makes things fail wtf.
matchers.forEach(function (name) {
  exports[name] = function (description, fn) {
    global[name](description, function () {
      var result = fn()
      if (result instanceof Promise) {
        waitsForPromise(function () { return result })
      }
    })
  }
})

function waitsForPromise (fn) {
  var promise = fn()
  waitsFor('spec promise to resolve', 30000, function (done) {
    promise.then(done, function (error) {
      jasmine.getEnv().currentSpec.fail(error)
      return done()
    })
  })
}

exports.waitsForPromise = waitsForPromise

// Lifted from Atom

exports.beforeEach = function (fn) {
  global.beforeEach(function () {
    var result = fn()
    if (result instanceof Promise) {
      waitsForPromise(function () { return result })
    }
  })
}

exports.afterEach = function (fn) {
  global.afterEach(function () {
    var result = fn()
    if (result instanceof Promise) {
      waitsForPromise(function () { return result })
    }
  })
}

exports.runs = function (fn) {
  global.runs(function () {
    var result = fn()
    if (result instanceof Promise) {
      waitsForPromise(function () { return result })
    }
  })
}

var matchers = ['it', 'fit', 'ffit', 'fffit'] // inlining this makes things fail wtf.
matchers.forEach(function (name) {
  exports[name] = function (description, fn) {
    global[name](description, function () {
      var result = fn()
      if (result instanceof Promise) {
        waitsForPromise(function () { return result })
      }
    })
  }
})

function waitsForPromise (fn) {
  var promise = fn()
  waitsFor('spec promise to resolve', 30000, function (done) {
    promise.then(done, function (error) {
      jasmine.getEnv().currentSpec.fail(error)
      return done()
    })
  })
}

exports.waitsForPromise = waitsForPromise

Make async `runs` a bit better.


'use strict';

var expect = require('chai').expect;
var validator = require('../');

describe('Date validator', function () {
  var simpleRules = {
    birthday: ['date-format:YYYY-MM-DD']
  };

  var getTestObject = function () {
    return {
      birthday: '1993-09-02',
    };
  };

  it('should success', function () {
    var result = validator.validate(simpleRules, getTestObject());
    var err = result.messages;

    expect(result.success).to.equal(true);
    expect(err).to.not.have.property('birthday');
  });

  it('should fail', function () {
    var testObj = getTestObject();
    testObj.birthday = '02-09-1993';
    var result = validator.validate(simpleRules, testObj);
    var err = result.messages;

    expect(result.success).to.equal(false);
    expect(err).to.have.property('birthday');
    expect(err.birthday.date).to.equal('Date format must conform YYYY-MM-DD.');
  });
});

'use strict';

var expect = require('chai').expect;
var validator = require('../');

describe('Date validator', function () {
  var simpleRules = {
    birthday: ['date-format:YYYY-MM-DD']
  };

  var getTestObject = function () {
    return {
      birthday: '1993-09-02',
    };
  };

  it('should success', function () {
    var result = validator.validate(simpleRules, getTestObject());
    var err = result.messages;

    expect(result.success).to.equal(true);
    expect(err).to.not.have.property('birthday');
  });

  it('should fail', function () {
    var testObj = getTestObject();
    testObj.birthday = '02-09-1993';
    var result = validator.validate(simpleRules, testObj);
    var err = result.messages;

    expect(result.success).to.equal(false);
    expect(err).to.have.property('birthday');
    expect(err.birthday['date-format:$1']).to.equal('Birthday must be in format YYYY-MM-DD.');
  });
});

Update test assertion on validation message


angular.module('horizonApp').directive('hrNumberPicker', function() {
  return {
    restrict: 'A',
    replace: true,
    scope: { initial_value: '=value' },
    templateUrl: '../../static/infrastructure/angular_templates/numberpicker.html',
    link: function(scope, element, attrs) {
      input = element.find('input').first();
      angular.forEach(element[0].attributes, function(attribute) {
        input_attr = input.attr(attribute.nodeName);
        if (typeof input_attr === 'undefined' || input_attr === false) {
          input.attr(attribute.nodeName, attribute.nodeValue);
        }
      });

      scope.value = scope.initial_value;
      scope.disabledInput = (angular.isDefined(attrs.readonly)) ? true : false;

      scope.disableArrow = function() {
        return (scope.value === 0) ? true : false;
      }

      scope.incrementValue = function() {
        if(!scope.disabledInput) {
          scope.value++;
        }
      }

      scope.decrementValue = function() {
        if(!scope.disabledInput && scope.value !== 0) {
          scope.value--;
        }
      }
    }
  };
})

angular.module('horizonApp').directive('hrNumberPicker', function() {
  return {
    restrict: 'A',
    replace: true,
    scope: { initial_value: '=value' },
    templateUrl: '../../static/infrastructure/angular_templates/numberpicker.html',
    link: function(scope, element, attrs) {
      input = element.find('input').first();
      angular.forEach(element[0].attributes, function(attribute) {
        input_attr = input.attr(attribute.nodeName);
        if (typeof input_attr === 'undefined' || input_attr === false) {
          input.attr(attribute.nodeName, attribute.nodeValue);
        }
      });

      scope.value = scope.initial_value;
      scope.disabledInput = (angular.isDefined(attrs.readonly)) ? true : false;

      scope.disableArrow = function() {
        return (scope.value === 0) ? true : false;
      };

      scope.incrementValue = function() {
        if(!scope.disabledInput) {
          scope.value++;
        }
      };

      scope.decrementValue = function() {
        if(!scope.disabledInput && scope.value !== 0) {
          scope.value--;
        }
      };
    }
  };
});

Add missing semicolons in js


const BaseAbility = require('./baseability.js');
const Costs = require('./costs.js');

class PlayAttachmentAction extends BaseAbility {
    constructor() {
        super({
            cost: [
                Costs.payReduceableFateCost('play'),
                Costs.playLimited()
            ],
            target: {
                cardCondition: (card, context) => context.source.owner.canAttach(context.source, card)
            }
        });
        this.title = 'PlayAttachmentAction';
    }

    meetsRequirements(context) {
        return (
            context.game.currentPhase !== 'dynasty' &&
            context.source.getType() === 'attachment' &&
            context.source.location === 'hand' &&
            context.source.canPlay()
        );
    }

    executeHandler(context) {
        context.player.attach(context.source, context.target);
    }

    isCardPlayed() {
        return true;
    }

    isCardAbility() {
        return false;
    }
}

module.exports = PlayAttachmentAction;

const BaseAbility = require('./baseability.js');
const Costs = require('./costs.js');

class PlayAttachmentAction extends BaseAbility {
    constructor() {
        super({
            cost: [
                Costs.payReduceableFateCost('play'),
                Costs.playLimited()
            ],
            target: {
                cardCondition: (card, context) => context.source.owner.canAttach(context.source, card)
            }
        });
        this.title = 'PlayAttachmentAction';
    }

    meetsRequirements(context) {
        return (
            context.game.currentPhase !== 'dynasty' &&
            context.source.getType() === 'attachment' &&
            context.source.location === 'hand' &&
            context.source.canPlay()
        );
    }

    executeHandler(context) {
        context.player.attach(context.source, context.target);
        context.game.addMessage('0} plays {1}, attaching it to {2}', context.player, context.source, context.target);
    }

    isCardPlayed() {
        return true;
    }

    isCardAbility() {
        return false;
    }
}

module.exports = PlayAttachmentAction;

Add game message to play attachment


define([
  'underscore',
  'jquery',
  'models/resource',
  'collections/bullet',
  'collections/paragraph'
], function (_, $, Resource, BulletCollection, ParagraphCollection) {
  'use strict';

  var ItemModel = Resource.extend({
    defaults: {
      name: '',
      title: '',
      heading: ''
    },

    resource: 'item',
    hasMany: ['bullets', 'paragraphs'],

    initialize: function(attributes, options) {
      this.set('bullets', new BulletCollection(
        attributes.bullets
      ));
      this.set('paragraphs', new ParagraphCollection(
        attributes.paragraphs
      ));
    },

    bulletIds: function() {
      return this.get('bullets').map(function(bullet) {
        return bullet.id;
      });
    },

    parse: function(response) {
      if (response.item) {
        return response.item;
      } else {
        return response;
      }
    }
  });

  return ItemModel;
});

define([
  'underscore',
  'jquery',
  'models/resource',
  'collections/bullet',
  'collections/paragraph'
], function (_, $, Resource, BulletCollection, ParagraphCollection) {
  'use strict';

  var ItemModel = Resource.extend({
    defaults: {
      name: '',
      title: '',
      heading: ''
    },

    resource: 'item',
    hasMany: ['bullets', 'paragraphs'],

    initialize: function(attributes, options) {
      this.set('bullets', new BulletCollection(
        attributes.bullets
      ));
      this.set('paragraphs', new ParagraphCollection(
        attributes.paragraphs
      ));
    },

    bulletIds: function() {
      return this.get('bullets').map(function(bullet) {
        return bullet.id;
      });
    },

    paragraphIds: function() {
      return this.get('paragraphs').map(function(paragraph) {
        return paragraph.id;
      });
    },

    parse: function(response) {
      if (response.item) {
        return response.item;
      } else {
        return response;
      }
    }
  });

  return ItemModel;
});

Add paragraphIds method to Item model.


import './sal.scss';

let options = {
  rootMargin: '0px',
  threshold: 0,
  animateClassName: 'sal-animate',
  selector: '[data-sal]',
  disableFor: null,
};

let elements = [];
let intersectionObserver = null;

const animate = element => (
  element.classList.add(options.animateClassName)
);

const isAnimated = element => (
  element.classList.contains(options.animateClassName)
);

const onIntersection = (entries, observer) => {
  entries.forEach((entry) => {
    if (entry.intersectionRatio > 0) {
      observer.unobserve(entry.target);
      animate(entry.target);
    }
  });
};

const disable = () => {
  intersectionObserver.disconnect();
  intersectionObserver = null;
};

const enable = () => {
  intersectionObserver = new IntersectionObserver(onIntersection, {
    rootMargin: options.rootMargin,
    threshold: options.threshold,
  });

  elements = [].filter.call(
    document.querySelectorAll(options.selector),
    element => !isAnimated(element, options.animateClassName),
  );

  elements.forEach(element => intersectionObserver.observe(element));
};

const init = (settings = options) => {
  if (settings !== options) {
    options = {
      ...options,
      ...settings,
    };
  }

  if (!options.disableFor) {
    enable();
  }

  return {
    elements,
    disable,
    enable,
  };
};

export default init;

import './sal.scss';

let options = {
  rootMargin: '0px',
  threshold: 0.5,
  animateClassName: 'sal-animate',
  selector: '[data-sal]',
  once: true,
  disableFor: null,
};

let elements = [];
let intersectionObserver = null;

const animate = element => (
  element.classList.add(options.animateClassName)
);

const reverse = element => (
  element.classList.remove(options.animateClassName)
);

const isAnimated = element => (
  element.classList.contains(options.animateClassName)
);

const onIntersection = (entries, observer) => {
  entries.forEach((entry) => {
    if (entry.intersectionRatio > 0) {
      animate(entry.target);

      if (!options.once) {
        observer.unobserve(entry.target);
      }
    } else if (!options.once) {
      reverse(entry.target);
    }
  });
};

const disable = () => {
  intersectionObserver.disconnect();
  intersectionObserver = null;
};

const enable = () => {
  intersectionObserver = new IntersectionObserver(onIntersection, {
    rootMargin: options.rootMargin,
    threshold: options.threshold,
  });

  elements = [].filter.call(
    document.querySelectorAll(options.selector),
    element => !isAnimated(element, options.animateClassName),
  );

  elements.forEach(element => intersectionObserver.observe(element));
};

const init = (settings = options) => {
  if (settings !== options) {
    options = {
      ...options,
      ...settings,
    };
  }

  if (!options.disableFor) {
    enable();
  }

  return {
    elements,
    disable,
    enable,
  };
};

export default init;

Add once option to manage working mode. Fix threshold value.


import fetch from 'node-fetch';
import { hashToHypen } from './formatText';
import { API_URL, HTTP_HEADERS } from '../constants';

const headers = HTTP_HEADERS;

const fetchStats = (battletag) => {
  const url = `${API_URL}/${hashToHypen(battletag)}/blob`;

  return fetch(encodeURI(url), { headers })
    .then((response) => response.json())
    .catch((error) => console.log(error));
};

export default fetchStats;

import fetch from 'node-fetch';
import { hashToHypen } from './formatText';
import { API_URL, HTTP_HEADERS } from '../constants';

const headers = HTTP_HEADERS;

const fetchStats = (battletag,platform) => {
  const url = `${API_URL}/${hashToHypen(battletag)}/blob?platform=${platform}`;

  return fetch(encodeURI(url), { headers })
    .then((response) => response.json())
    .catch((error) => console.log(error));
};

export default fetchStats;

Add console support, p. 2


$(function () {
    'use strict';
    $.fn.getDateRangeSeparator = function () {
        return ' to ';
    };
    $.fn.createBootstrap3DateRangePicker = function(
        range_labels, separator, startdate, enddate
    ) {
        var now = moment();
        var ranges = {};
        ranges[range_labels.last_7_days] = [
            moment().subtract('7', 'days').startOf('days')
        ];

        ranges[range_labels.last_month] = [
            moment().subtract('1', 'months').startOf('month'),
            moment().subtract('1', 'months').endOf('month')
        ];

        ranges[range_labels.last_30_days] = [
            moment().subtract('30', 'days').startOf('days')
        ];
        var config = {
            showDropdowns: true,
            ranges: ranges,
            locale: {
                format: 'YYYY-MM-DD',
                separator: separator
            }
        };
        if (!_.isEmpty(startdate) && !_.isEmpty(enddate)) {
            config.startDate = new Date(startdate);
            config.endDate = new Date(enddate);
        }
        $(this).daterangepicker(config);
    };
    $.fn.createBootstrap3DefaultDateRangePicker = function () {
        this.createBootstrap3DateRangePicker(
            {
                last_7_days: 'Last 7 Days',
                last_month: 'Last Month',
                last_30_days: 'Last 30 Days'
            },
            this.getDateRangeSeparator()
        );
    };
});

$(function () {
    'use strict';
    $.fn.getDateRangeSeparator = function () {
        return ' to ';
    };
    $.fn.createBootstrap3DateRangePicker = function(
        range_labels, separator, startdate, enddate
    ) {
        var now = moment();
        var ranges = {};
        ranges[range_labels.last_7_days] = [
            moment().subtract('7', 'days').startOf('days')
        ];

        ranges[range_labels.last_month] = [
            moment().subtract('1', 'months').startOf('month'),
            moment().subtract('1', 'months').endOf('month')
        ];

        ranges[range_labels.last_30_days] = [
            moment().subtract('30', 'days').startOf('days')
        ];
        var config = {
            showDropdowns: true,
            ranges: ranges,
            locale: {
                format: 'YYYY-MM-DD',
                separator: separator
            }
        };
        var hasStartAndEndDate = !_.isEmpty(startdate) && !_.isEmpty(enddate);
        if (hasStartAndEndDate) {
            config.startDate = new Date(startdate);
            config.endDate = new Date(enddate);
        }

        $(this).daterangepicker(config);

        if (! hasStartAndEndDate){
            $(this).val("");
        }
    };
    $.fn.createBootstrap3DefaultDateRangePicker = function () {
        this.createBootstrap3DateRangePicker(
            {
                last_7_days: 'Last 7 Days',
                last_month: 'Last Month',
                last_30_days: 'Last 30 Days'
            },
            this.getDateRangeSeparator()
        );
    };
});

Clear date range picker if no start and end date given


export default [
    {title: 'Animal'},
    {title: 'Effects'},
    {title: 'Loops'},
    {title: 'Notes'},
    {title: 'Percussion'},
    {title: 'Space'},
    {title: 'Sports'},
    {title: 'Voice'},
    {title: 'Wacky'}
];

export default [
    {title: 'Animals'},
    {title: 'Effects'},
    {title: 'Loops'},
    {title: 'Notes'},
    {title: 'Percussion'},
    {title: 'Space'},
    {title: 'Sports'},
    {title: 'Voice'},
    {title: 'Wacky'}
];

Fix category label for sound library


/**
 * Configuration module
 */
var path        = require('path');
var fs          = require('fs');
var lodash      = require('lodash');
var jsonlint    = require('json-lint');

var print       = require('./print.js');

module.exports = {
    read: read
};

var defaultConfig = {
    'source': './',
    'destination': './situs',
    'ignore': [
        'node_modules/**/*'
    ],
    'port': 4000,
    'noConfig': false,
    'global': {}
};

function read(filePath, callback) {

    fs.exists(filePath, function (exists) {

        if (!exists) {

            // Add compiled dir in ignore list
            defaultConfig.ignore.push('situs/**/*');
            defaultConfig.noConfig = true;

            return callback(null, defaultConfig);
        }

        fs.readFile(filePath, 'utf8', function (error, data) {

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

            var lint = jsonlint(data, {comments:false});

            if (lint.error) {
                error = 'Syntax error on situs.json:'+lint.line+'.\n'+lint.error;
                return callback(error);
            }

            var obj = lodash.extend(defaultConfig, JSON.parse(data));

            // Add compiled dir in ignore list
            obj.ignore.push('!'+path.normalize(obj.destination)+'/**/*');

            return callback(null, obj);
        });

    });
}

/**
 * Configuration module
 */
var path        = require('path');
var fs          = require('fs');
var lodash      = require('lodash');
var jsonlint    = require('json-lint');

var print       = require('./print.js');

module.exports = {
    read: read
};

var defaultConfig = {
    'source': './',
    'destination': './situs',
    'ignore': [
        'node_modules/**/*'
    ],
    'port': 4000,
    'noConfig': false,
    'global': {}
};

function read(filePath, callback) {

    fs.exists(filePath, function (exists) {

        if (!exists) {

            // Add compiled dir in ignore list
            defaultConfig.ignore.push('situs/**/*');
            defaultConfig.noConfig = true;

            return callback(null, defaultConfig);
        }

        fs.readFile(filePath, 'utf8', function (error, data) {

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

            var lint = jsonlint(data, {comments:false});

            if (lint.error) {
                error = 'Syntax error on situs.json:'+lint.line+'.\n'+lint.error;
                return callback(error);
            }

            var obj = lodash.extend(defaultConfig, JSON.parse(data));

            // Add compiled dir in ignore list
            obj.ignore.push(path.normalize(obj.destination)+'/**/*');

            return callback(null, obj);
        });

    });
}

Fix additional "!" when push ignore list


var logger = require('../utils/logger')('MessageComparator'),
    Response = require('./response');

/**
 * Represents a MessageComparator capable of determining whether an
 * incoming message must be relayed to a module or not.
 * @param {RegExp}      regex       Regex to which every incoming
 *                                  message will be tested against
 * @param {Function}    callback    Callback function that will be called
 *                                  when a given message is matched by the
 *                                  provided regex
 * @constructor
 */
var MessageComparator = function(regex, callback) {
    this.regex = regex;
    this.callback = callback;
    logger.verbose('Registered message: ' + regex);
};

MessageComparator.prototype = {
    /**
     * Checks and executes the callback on a given message in an Envelope
     * @param  {Envelope}   envelope    Envelope containing a message
     *                                  to be checked
     * @return {Bool}                   Whether the callback was called or not
     */
    call: function(envelope) {
        var match = this.regex.exec(envelope.text);
        if(!!match) {
            logger.verbose('Message ' + envelope.text + ' matched regex ' + this.regex);
            envelope.stopPropagation = true;
            this.callback(new Response(envelope, match));
            return true;
        }
        return false;
    }
};

module.exports = MessageComparator;

var logger = require('../utils/logger')('MessageComparator'),
    Response = require('./response');

/**
 * Represents a MessageComparator capable of determining whether an
 * incoming message must be relayed to a module or not.
 * @param {RegExp}      regex       Regex to which every incoming
 *                                  message will be tested against
 * @param {Function}    callback    Callback function that will be called
 *                                  when a given message is matched by the
 *                                  provided regex
 * @constructor
 */
var MessageComparator = function(moduleName, regex, callback) {
    this.regex = regex;
    this.callback = callback;
    this.moduleName = moduleName;
    this.suspended = false;
    logger.verbose('Registered message: ' + regex);
};

MessageComparator.prototype = {
    /**
     * Checks and executes the callback on a given message in an Envelope
     * @param  {Envelope}   envelope    Envelope containing a message
     *                                  to be checked
     * @return {Bool}                   Whether the callback was called or not
     */
    call: function(envelope) {
        if(this.suspended) {
            return false;
        }
        var match = this.regex.exec(envelope.text);
        if(!!match) {
            logger.verbose('Message ' + envelope.text + ' matched regex ' + this.regex);
            envelope.stopPropagation = true;
            this.callback(new Response(envelope, match));
            return true;
        }
        return false;
    },

    /**
     * Sets the suspension state of this MessageComparator. Setting it to true, prevents it
     * from matching messages and calling its module.
     * @param {Boolean} newState New suspension state of this MessageComparator
     * @since 2.0
     */
    setSuspensionState: function(newState) {
        this.suspended = newState;
    }
};

module.exports = MessageComparator;

Add suspension capabilities to MessageComparator


module.exports = function (creator, fixtureName) {
    return creator.createFixture('/episodes/p00y1h7j/recommendations').then(function (fixture) {
        fixture.save(fixtureName);
    });
};

module.exports = function (creator, fixtureName) {
    return creator.createFixture('/episodes/p00y1h7j/recommendations').then(function (fixture) {
        fixture.addEpisode();

        fixture.save(fixtureName);
    });
};

Fix recommendations fixture for when no recomendations are returned by iBL


/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    // Task configuration.
    watch: {
      lib_test: {
        files: [ 'styles/**/*.css', '*.html' ],
        tasks: [],
        options: {
        	livereload: true
        }
      }
    }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-watch');

  // Default task.
  grunt.registerTask('default', ['watch']);

};

/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    // Task configuration.
    watch: {
      lib_test: {
        files: [ '*.css', '*.html' ],
        tasks: [],
        options: {
        	livereload: true
        }
      }
    }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-watch');

  // Default task.
  grunt.registerTask('default', ['watch']);

};

Watch styles in main folder


/**
 * Settings notice component.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import PropTypes from 'prop-types';

export default function SettingsNotice( { message } ) {
	if ( ! message ) {
		return null;
	}

	return (

				{ message }

	);
}

SettingsNotice.propTypes = {
	message: PropTypes.string.isRequired,
};

/**
 * Settings notice component.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import PropTypes from 'prop-types';
import classnames from 'classnames';

export default function SettingsNotice( { message, isSuggestion } ) {
	if ( ! message ) {
		return null;
	}

	return (
		<div
			className={ classnames(
				'googlesitekit-settings-notice',
				{ 'googlesitekit-settings-notice--suggestion': isSuggestion }
			) }
		>
			<div className="googlesitekit-settings-notice__text">
				{ message }
			</div>
		</div>
	);
}

SettingsNotice.propTypes = {
	message: PropTypes.string.isRequired,
	isSuggestion: PropTypes.bool,
};

Add support for suggestion style to SettingsNotice component.


'use strict';

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

module.exports = function(newWindow) {
  if (newWindow) {
    var launchPackagerScript =
      path.resolve(__dirname, '..', 'packager', 'launchPackager.command');
    if (process.platform === 'darwin') {
      child_process.spawnSync('open', [launchPackagerScript]);
    } else if (process.platform === 'linux') {
      child_process.spawn(
        'xterm',
        ['-e', 'sh', launchPackagerScript],
        {detached: true});
    }
  } else {
    child_process.spawn('sh', [
        path.resolve(__dirname, '..', 'packager', 'packager.sh'),
        '--projectRoots',
        process.cwd(),
      ], {stdio: 'inherit'});
  }
};

'use strict';

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

module.exports = function(newWindow) {
  if (newWindow) {
    var launchPackagerScript =
      path.resolve(__dirname, '..', 'packager', 'launchPackager.command');
    if (process.platform === 'darwin') {
      child_process.spawnSync('open', [launchPackagerScript]);
    } else if (process.platform === 'linux') {
      child_process.spawn(
        'xterm',
        ['-e', 'sh', launchPackagerScript],
        {detached: true});
    }
  } else {
    if (/^win/.test(process.platform)) {
      child_process.spawn('node', [
        path.resolve(__dirname, '..', 'packager', 'packager.js'),
        '--projectRoots',
        process.cwd(),
        ], {stdio: 'inherit'});
    } else {
      child_process.spawn('sh', [
          path.resolve(__dirname, '..', 'packager', 'packager.sh'),
          '--projectRoots',
          process.cwd(),
        ], {stdio: 'inherit'});
    }
  }
};

Fix 'react-native start' on Windows


import promiseProps from 'promise-props-recursive'
import getLocalVersion from '../../npm-bridge/getLocalVersion'
import getGlobalSanityCliVersion from '../../util/getGlobalSanityCliVersion'
import pkg from '../../../package.json'

export default {
  name: 'version',
  signature: 'version',
  description: 'Shows the installed versions of core Sanity components',
  action: ({print}) => {
    promiseProps({
      local: getLocalVersion(pkg.name),
      global: getGlobalSanityCliVersion()
    }).then(versions => {
      if (versions.global) {
        print(`${pkg.name} (global): ${versions.global}`)
      }

      if (versions.local) {
        print(`${pkg.name} (local): ${versions.local}`)
      }
    })
  }
}

import promiseProps from 'promise-props-recursive'
import getLocalVersion from '../../npm-bridge/getLocalVersion'
import getGlobalSanityCliVersion from '../../util/getGlobalSanityCliVersion'
import pkg from '../../../package.json'

export default {
  name: 'version',
  signature: 'version',
  description: 'Shows the installed versions of core Sanity components',
  action: ({print}) =>
    promiseProps({
      local: getLocalVersion(pkg.name),
      global: getGlobalSanityCliVersion()
    }).then(versions => {
      if (versions.global) {
        print(`${pkg.name} (global): ${versions.global}`)
      }

      if (versions.local) {
        print(`${pkg.name} (local): ${versions.local}`)
      }
    })
}

Return promise from version command