// These URL paths will be transformed to CN mirrors.
var mirrors = {
  "//developers.google.com" : "//developers.google.cn",
  "//firebase.google.com"   : "//firebase.google.cn",
  "//developer.android.com" : "//developer.android.google.cn",
  "//angular.io"            : "//angular.cn",
  "//maps.google.com"       : "//maps.google.cn",
  "google.com/maps"         : "google.cn/maps",
}

// These URL paths are not available on CN mirrors, therefore won't be transformed.
var whitelist = [
  "//developers.google.com/groups",
  "//developers.google.com/events",
  "//firebase.google.com/support/contact/",
]

function mirrorUrl(url) {
  // Check for whitelisting.
  for (var key in whitelist) {
    if (url.includes(whitelist[key])) {
      return url;
    }
  }

  // Check for mapping.
  for (var key in mirrors) {
  	if (url.includes(key)) {
  		url = url.replace(key, mirrors[key]);
  		break;
  	}
  }
  return url;
}

// These URL paths will be transformed to CN mirrors.
var mirrors = {
  "//developers.google.com" : "//developers.google.cn",
  "//firebase.google.com"   : "//firebase.google.cn",
  "//developer.android.com" : "//developer.android.google.cn",
  "//angular.io"            : "//angular.cn",
  "//maps.google.com"       : "//maps.google.cn",
  "google.com/maps"         : "google.cn/maps",
  "//translate.google.com"  : "//translate.google.cn",
}

// These URL paths are not available on CN mirrors, therefore won't be transformed.
var whitelist = [
  "//developers.google.com/groups",
  "//developers.google.com/events",
  "//firebase.google.com/support/contact/",
]

function mirrorUrl(url) {
  // Check for whitelisting.
  for (var key in whitelist) {
    if (url.includes(whitelist[key])) {
      return url;
    }
  }

  // Check for mapping.
  for (var key in mirrors) {
  	if (url.includes(key)) {
  		url = url.replace(key, mirrors[key]);
  		break;
  	}
  }
  return url;
}

Add mapping for Google Translate service


function add(a,b){"use strict";return a+b}function multiply(a,b){"use strict";return a*b}function divide(a,b){"use strict";try{return multiply(add(a,b),a,b)/c}catch(e){Raven.captureException(e)}}
//@ sourceMappingURL=file.sourcemap.js

function add(a,b){"use strict";return a+b}function multiply(a,b){"use strict";return a*b}function divide(a,b){"use strict";try{return multiply(add(a,b),a,b)/c}catch(e){Raven.captureException(e)}}
//# sourceMappingURL=file.sourcemap.js

Update sourcemappingurl declaration in example


md.headers = ({storage: {state}, detect}) => {

  var callback = ({method, url, responseHeaders}) => {
    if (method !== 'GET') {
      return {responseHeaders}
    }

    var header = responseHeaders.find(({name}) => /content-type/i.test(name)) || {}
    var origin = detect.match(url)

    if (!detect.header(header.value) && !origin) {
      return {responseHeaders}
    }

    if (origin.csp) {
      responseHeaders = responseHeaders
        .filter(({name}) => !/content-security-policy/i.test(name))
    }

    // ff: markdown `content-type` is not allowed
    if (/Firefox/.test(navigator.userAgent) && detect.header(header.value)) {
      header.value = 'text/plain'
    }

    if (origin.encoding && header.name) {
      var [media] = header.value.split(';')
      header.value = `${media}; charset=${origin.encoding}`
    }

    return {responseHeaders}
  }

  var filter = {
    urls: ['<all_urls>'],
    types: ['main_frame', 'sub_frame']
  }

  var options = ['blocking', 'responseHeaders']

  var add = () => {
    if (!chrome.webRequest.onHeadersReceived.hasListeners()) {
      chrome.webRequest.onHeadersReceived.addListener(callback, filter, options)
    }
  }

  var remove = () => {
    chrome.webRequest.onHeadersReceived.removeListener(callback, filter, options)
  }

  return {add, remove}
}

md.headers = ({storage: {state}, detect}) => {

  var callback = ({method, url, responseHeaders}) => {
    if (method !== 'GET') {
      return {responseHeaders}
    }

    var header = responseHeaders.find(({name}) => /^content-type/i.test(name)) || {}
    var origin = detect.match(url)

    if (!detect.header(header.value) && !origin) {
      return {responseHeaders}
    }

    if (origin.csp) {
      responseHeaders = responseHeaders
        .filter(({name}) => !/content-security-policy/i.test(name))
    }

    // ff: markdown `content-type` is not allowed
    if (/Firefox/.test(navigator.userAgent) && detect.header(header.value)) {
      header.value = 'text/plain'
    }

    if (origin.encoding && header.name) {
      var [media] = header.value.split(';')
      header.value = `${media}; charset=${origin.encoding}`
    }

    return {responseHeaders}
  }

  var filter = {
    urls: ['<all_urls>'],
    types: ['main_frame', 'sub_frame']
  }

  var options = ['blocking', 'responseHeaders']

  var add = () => {
    if (!chrome.webRequest.onHeadersReceived.hasListener(callback)) {
      chrome.webRequest.onHeadersReceived.addListener(callback, filter, options)
    }
  }

  var remove = () => {
    chrome.webRequest.onHeadersReceived.removeListener(callback, filter, options)
  }

  return {add, remove}
}

Improve content-type header detection + Fix webRequest listener check for ff


angular.module('progressButtonDemo', ['progressButton'])
	.controller('ProgressButtonsCtrl', function($scope, $interval) {
		$scope.buttons = [
			{ value: 0.0 },
			{ value: 0.0 },
			{ value: 0.0 }
		]

		$scope.simulateProgress = function(buttonIndex, seconds, callback) {
			if($scope.buttons[buttonIndex].simulating) return

			$scope.buttons[buttonIndex].simulating = true
			$scope.buttons[buttonIndex].value = 0.2

			var interval = $interval(function() {
				$scope.buttons[buttonIndex].value += 0.2

				if($scope.buttons[buttonIndex].value >= 1.0) {
					$interval.cancel(interval)

					if(typeof callback === 'function') callback()
				}
			}, (seconds / 5) * 1000)
		}

		$scope.generate = function(button) {
			button.progressTimed(3, function() {
				$scope.generate = function() {
					alert('Showing how a callback works!');
				}
			})
		}
	})
	.controller('ProgressControlCtrl', function($scope, $timeout) {
		$scope.controlButton = {}

		$timeout(function() {
			$scope.increment = function(value) {
				$scope.controlButton.progressIncrement(value)
			}

			$scope.setTo = function(value) {
				$scope.controlButton.progressSet(value)
			}

			$scope.finish = function() {
				$scope.controlButton.progressFinish()
			}
		})
	})

angular.module('progressButtonDemo', ['progressButton'])
	.controller('ProgressButtonsCtrl', function($scope, $interval) {
		$scope.buttons = [
			{ value: 0.0 },
			{ value: 0.0 },
			{ value: 0.0 }
		]

		$scope.simulateProgress = function(buttonIndex, seconds, callback) {
			if($scope.buttons[buttonIndex].simulating) return

			$scope.buttons[buttonIndex].simulating = true
			$scope.buttons[buttonIndex].value = 0.2

			var interval = $interval(function() {
				$scope.buttons[buttonIndex].value += 0.2

				if($scope.buttons[buttonIndex].value >= 1.0) {
					$interval.cancel(interval)

					if(typeof callback === 'function') callback()
				}
			}, (seconds / 5) * 1000)
		}

		$scope.generate = function(button) {
			button.progressTimed(3, function() {
				$scope.generate = function() {
					alert('Showing how a callback works!');
				}
			})
		}
	})
	.controller('ProgressControlCtrl', function($scope) {
	})

Remove old control button code


'use strict';

var etcdApp = angular.module('etcdApp', ['ngRoute', 'etcdResource', 'timeRelative'])
  .config(['$routeProvider', function ($routeProvider) {
    //read localstorage
    var previous_path = localStorage.getItem('etcd_path');
    if(previous_path != null) {
      var redirect_path = previous_path
    } else {
      var redirect_path = '/v1/keys/';
    }
    $routeProvider
      .when('/', {
        redirectTo: redirect_path
      })
      .otherwise({
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
      });
  }]);

'use strict';

var etcdApp = angular.module('etcdApp', ['ngRoute', 'etcdResource', 'timeRelative'])
  .config(['$routeProvider', function ($routeProvider) {
    //read localstorage
    var previous_path = localStorage.getItem('etcd_path');
    if(previous_path != null && previous_path.indexOf('/v1/keys/') !== -1) {
      var redirect_path = previous_path
    } else {
      var redirect_path = '/v1/keys/';
    }
    $routeProvider
      .when('/', {
        redirectTo: redirect_path
      })
      .otherwise({
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
      });
  }]);

Use localstorage location only if it is valid


var DGT = require('..');
var board = new DGT.Board('/dev/ttyUSB0');

board.on('ready', function() {
  console.log('Serial No:', board.serialNo);
  console.log('Version:', board.versionNo);
  console.log('-----');
});

board.on('data', function(data) {
  console.log('Field:', data.field);
  console.log('Piece:', data.piece);
  console.log('-----');
});

/**
 * TODO:
 */

/*
board.on('move', function(move) {

});

board.on('end', function(result) {

});
 */

var DGT = require('..');
var board = new DGT.Board('/dev/ttyUSB0');

board.on('ready', function() {
  console.log('Serial No:', board.serialNo);
  console.log('Version:', board.versionNo);
  console.log(board.chess.ascii());
  console.log('-----');

});

board.on('data', function(data) {
  console.log('Field:', data.field);
  console.log('Piece:', data.piece);
  console.log('-----');
});

/**
 * TODO:
 */

/*
board.on('move', function(move) {

});

board.on('end', function(result) {

});
 */

Add output of ASCII chess board


/* global Raven */

import config from '../config/environment';
import Ember from 'ember';

export function initialize() {
  var _onerror;

  Raven.config(config.sentry.dsn, {
    whitelistUrls: config.sentry.whitelistUrls
  }).install();

  _onerror = Ember.onerror;
  Ember.onerror = function(error){
    Raven.captureException(error);
    if (typeof _onerror === 'function') {
      _onerror.call(this, error);
    }
  }

  Ember.RSVP.configure('onerror', function(error){
    Raven.captureException(error);
  });
}

export default {
  name: 'sentry-setup',
  initialize: initialize
};

/* global Raven */

import config from '../config/environment';
import Ember from 'ember';

export function initialize() {
  // Disable for development
  if (config.sentry.development) {
    return;
  }

  var _onerror;

  Raven.config(config.sentry.dsn, {
    whitelistUrls: config.sentry.whitelistUrls
  }).install();

  _onerror = Ember.onerror;
  Ember.onerror = function(error){
    Raven.captureException(error);
    if (typeof _onerror === 'function') {
      _onerror.call(this, error);
    }
  }

  Ember.RSVP.configure('onerror', function(error){
    Raven.captureException(error);
  });
}

export default {
  name: 'sentry-setup',
  initialize: initialize
};

Check if we want to disable and return


'use strict';

angular.module('quakeStatsApp')
    .controller('MainCtrl', ['$scope',
        function ($scope) {
    }]);

'use strict';

angular.module('quakeStatsApp')
    .controller('MainCtrl', [
        function () {
        }
    ]
);

Tidy Up: JSHint clean up


module.exports = function (grunt) {

    var url = grunt.option('url') || 'http://localhost:3000';

    // Project configuration.
    grunt.initConfig({
        bowercopy: {
            options: {
                clean: false
            },
            js: {
                options: {
                    destPrefix: 'js/vendor'
                },
                files: {
                    'jquery.min.js': 'jquery/dist/jquery.min.js',
                    'bootstrap.min.js': 'bootstrap/dist/js/bootstrap.min.js',
                    'easeljs-0.7.1.min.js': 'easeljs/lib/easeljs-0.7.1.min.js'
                }
            },
            css: {
                options: {
                    destPrefix: 'css/vendor'
                },
                files: {
                    'bootstrap.min.css': 'bootstrap/dist/css/bootstrap.min.css',
                    'bootstrap.css.map': 'bootstrap/dist/css/bootstrap.css.map'
                }
            }
        },
        'string-replace': {
            dist: {
                files: {
                    './': ['index.html', 'js/ui.js']
                },
                options: {
                    replacements: [{
                        pattern: /http:\/\/\s*(.+?)\s*:3000/,
                        replacement: url
                    }]
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-bowercopy');
    grunt.loadNpmTasks('grunt-string-replace');

    grunt.registerTask('default', ['bowercopy', 'string-replace']);

};

module.exports = function (grunt) {

    var url = grunt.option('url') || 'http://localhost:3000';

    // Project configuration.
    grunt.initConfig({
        bowercopy: {
            options: {
                clean: true
            },
            js: {
                options: {
                    destPrefix: 'js/vendor'
                },
                files: {
                    'jquery.min.js': 'jquery/dist/jquery.min.js',
                    'bootstrap.min.js': 'bootstrap/dist/js/bootstrap.min.js',
                    'easeljs-0.7.1.min.js': 'easeljs/lib/easeljs-0.7.1.min.js'
                }
            },
            css: {
                options: {
                    destPrefix: 'css/vendor'
                },
                files: {
                    'bootstrap.min.css': 'bootstrap/dist/css/bootstrap.min.css',
                    'bootstrap.css.map': 'bootstrap/dist/css/bootstrap.css.map'
                }
            }
        },
        'string-replace': {
            dist: {
                files: {
                    './': ['index.html', 'js/ui.js']
                },
                options: {
                    replacements: [{
                        pattern: /http:\/\/\s*(.+?)\s*:3000/,
                        replacement: url
                    }]
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-bowercopy');
    grunt.loadNpmTasks('grunt-string-replace');

    grunt.registerTask('default', ['bowercopy', 'string-replace']);

};

Edit grunt command: Add --url parameter


"use strict";

$(() => {
    // This code will be executed when the user visits /login and
    // dev_login.html is rendered.
    if ($("[data-page-id='dev-login']").length > 0) {
        if (window.location.hash.substring(0, 1) === "#") {
            /* We append the location.hash to the formaction so that URL can be
            preserved after user is logged in. See this:
            https://stackoverflow.com/questions/5283395/url-hash-is-persisting-between-redirects */
            $("input[name='direct_email']").each(function () {
                const new_formaction = $(this).attr("formaction") + "/" + window.location.hash;
                $(this).attr("formaction", new_formaction);
            });
        }
    }
});

"use strict";

$(() => {
    // This code will be executed when the user visits /login and
    // dev_login.html is rendered.
    if ($("[data-page-id='dev-login']").length > 0) {
        if (window.location.hash.substring(0, 1) === "#") {
            /* We append the location.hash to the input field with name next so that URL can be
            preserved after user is logged in. See this:
            https://stackoverflow.com/questions/5283395/url-hash-is-persisting-between-redirects */
            $("input[name='next']").each(function () {
                const new_value = $(this).attr("value") + window.location.hash;
                $(this).attr("value", new_value);
            });
        }
    }
});

Fix handling of fragments in the URL.


const defer = () => {
  let state = false // Resolved or not
  let value
  const callbacks = []
  const resolve = val => {
    if (state) {
      return
    }

    state = true
    value = val
    callbacks.forEach(cb => {
      cb(val)
    })
  }

  const then = cb => {
    if (!state) {
      callbacks.push(cb)
      return
    }
    cb(value)
  }

  const deferred = {
    resolved () {
      return state
    },
    resolve,
    promise: {
      then
    }
  }
  return deferred
}

export default defer

const defer = () => {
  let state = false // Resolved or not
  let value
  const callbacks = []
  const resolve = val => {
    if (state) {
      return
    }

    state = true
    value = val
    for (let i = 0, len = callbacks.length; i < len; i++) {
      callbacks[i](val)
    }
  }

  const then = cb => {
    if (!state) {
      callbacks.push(cb)
      return
    }
    cb(value)
  }

  const deferred = {
    resolved () {
      return state
    },
    resolve,
    promise: {
      then
    }
  }
  return deferred
}

export default defer

Use for-loop instead of forEach


module("About Operators (topics/about_operators.js)");

test("addition", function() {
  var result = 0;
  //starting i at 0, add i to result and increment i by 1 until i is equal to 5
  for (var i = 0; i <= 5; i++) {
    result = result + i;
  }
  equal(__, result, "What is the value of result?");
});

test("assignment addition", function() {
  var result = 0;
  for (var i = 0; i <=5; i++) {
    //the code below is just like saying result = result + i; but is more concise
    result += i;
  }
  equal(__, result, "What is the value of result?");
});

test("subtraction", function() {
  var result = 5;
  for (var i = 0; i <= 2; i++) {
    result = result - i;
  }
  equal(__, result, "What is the value of result?");
});

test("assignment subtraction", function() {
  var result = 5;
  for (var i = 0; i <= 2; i++) {
    result -= i;
  }
  equal(__, result, "What is the value of result?");
});

//Assignment operators are available for multiplication and division as well
//let's do one more, the modulo operator, used for showing division remainder

test("modulus", function() {
  var result = 10;
  var x = 5;
  //again this is exactly the same as result = result % x
  result %= x;
  equal(__, result, "What is the value of result?");
});

module("About Operators (topics/about_operators.js)");

test("addition", function() {
  var result = 0;
  //starting i at 0, add i to result and increment i by 1 until i is equal to 5
  for (var i = 0; i <= 5; i++) {
    result = result + i;
  }
  equal(15, result, "What is the value of result?");
});

test("assignment addition", function() {
  var result = 0;
  for (var i = 0; i <=5; i++) {
    //the code below is just like saying result = result + i; but is more concise
    result += i;
  }
  equal(15, result, "What is the value of result?");
});

test("subtraction", function() {
  var result = 5;
  for (var i = 0; i <= 2; i++) {
    result = result - i;
  }
  equal(2, result, "What is the value of result?");
});

test("assignment subtraction", function() {
  var result = 5;
  for (var i = 0; i <= 2; i++) {
    result -= i;
  }
  equal(2, result, "What is the value of result?");
});

//Assignment operators are available for multiplication and division as well
//let's do one more, the modulo operator, used for showing division remainder

test("modulus", function() {
  var result = 10;
  var x = 5;
  //again this is exactly the same as result = result % x
  result %= x;
  equal(0, result, "What is the value of result?");
});

Complete operators tests to pass


import React from 'react'
import { fieldInstanceBoost, scoreInstanceBoost } from 'sajari'

import Base from './Base.js'
import Components from './constants/QueryComponentConstants.js'

const IndexBoost = props => {
  const { data, namespace, ...others } = props
  return (
    <Base
      {...others}
      runDefault='all'
      componentName={Components.INSTANCE_BOOSTS}
      data={data}
      namespace={namespace}
    />
  )
}

IndexBoost.propTypes = {
  data: React.PropTypes.object.isRequired,
  namespace: React.PropTypes.string,
}

const FieldInstanceBoost = props => {
  const { field, value, namespace, ...others } = props
  return (
    <IndexBoost
      {...others}
      data={fieldInstanceBoost(field, value)}
      namespace={namespace}
    />
  )
}

FieldInstanceBoost.propTypes = {
  field: React.PropTypes.string.isRequired,
  value: React.PropTypes.any.isRequired,
  namespace: React.PropTypes.string,
}

const ScoreInstanceBoost = props => {
  const { threshold, minCount, namespace, ...others } = props
  return (
    <IndexBoost
      {...others}
      data={scoreInstanceBoost(threshold, minCount)}
      namespace={namespace}
    />
  )
}

ScoreInstanceBoost.propTypes = {
  threshold: React.PropTypes.number.isRequired,
  min_count: React.PropTypes.number.isRequired,
  namespace: React.PropTypes.string,
}

export { FieldInstanceBoost, ScoreInstanceBoost }

import React from 'react'
import { fieldInstanceBoost, scoreInstanceBoost } from 'sajari'

import Base from './Base.js'
import Components from './constants/QueryComponentConstants.js'

const IndexBoost = props => {
  const { data, namespace, ...others } = props
  return (
    <Base
      {...others}
      runDefault='all'
      componentName={Components.INSTANCE_BOOSTS}
      data={data}
      namespace={namespace}
    />
  )
}

IndexBoost.propTypes = {
  data: React.PropTypes.object.isRequired,
  namespace: React.PropTypes.string,
}

const FieldInstanceBoost = props => {
  const { field, value, namespace, ...others } = props
  return (
    <IndexBoost
      {...others}
      data={fieldInstanceBoost(field, value)}
      namespace={namespace}
    />
  )
}

FieldInstanceBoost.propTypes = {
  field: React.PropTypes.string.isRequired,
  value: React.PropTypes.any.isRequired,
  namespace: React.PropTypes.string,
}

const ScoreInstanceBoost = props => {
  const { threshold, minCount, namespace, ...others } = props
  return (
    <IndexBoost
      {...others}
      data={scoreInstanceBoost(threshold, minCount)}
      namespace={namespace}
    />
  )
}

ScoreInstanceBoost.propTypes = {
  threshold: React.PropTypes.number.isRequired,
  minCount: React.PropTypes.number.isRequired,
  namespace: React.PropTypes.string,
}

export { FieldInstanceBoost, ScoreInstanceBoost }

Fix incorrect proptype name on ScoreInstanceBoost


const makersList = require('../../fixtures/makers');
module.exports = (related, options) => {
  // If any of these roles match, see more button will link to
  // search page with 'maker' filter selected as the person
  // If not, it will link to object search page with the person as the query
  // var makerRoles = ['creator', 'designer', 'engineer', 'inventor'];
  var makerRoles = makersList;

  if (related.length > 0 && related.some(el => makerRoles.indexOf(el.role.toLowerCase()) > -1)) {
    return options.fn(options.data.root);
  } else {
    return options.inverse(options.data.root);
  }
};

const makersList = require('../../fixtures/makers');
module.exports = (related, options) => {
  // If any of these roles match, see more button will link to
  // search page with 'maker' filter selected as the person
  // If not, it will link to object search page with the person as the query
  // var makerRoles = ['creator', 'designer', 'engineer', 'inventor'];
  var makerRoles = makersList;

  if (related.length > 0 && related.some(el => makerRoles.indexOf(el.role && el.role.toLowerCase()) > -1)) {
    return options.fn(options.data.root);
  } else {
    return options.inverse(options.data.root);
  }
};

Check existence of role before lowercasing it


export default (history, catchAllHandler) => {
  return next => (reducer, initialState) => {
    const store = next(reducer, initialState);

    return {
      ...store,
      dispatch(action) {
        const { type, meta } = action;
        const transitionMetaFunc = meta ?
          (meta.transition || catchAllHandler) :
          catchAllHandler;

        const prevState = transitionMetaFunc && store.getState();

        store.dispatch(action);

        const nextState = transitionMetaFunc && store.getState();

        let transitionData = transitionMetaFunc && (
          transitionMetaFunc(prevState, nextState, action)
        );

        if (transitionData) {
          const method = transitionData.replace ? 'replace' : 'push';
          history[method](transitionData);
        }

        return action;
      }
    };
  };
}

export const executeTransition = history => transitionData => {
  if (transitionData) {
    const method = transitionData.replace ? 'replace' : 'push';
    history[method](transitionData);
  }
};

export default (history, catchAllHandler) => next => (reducer, initialState) => {
  const store = next(reducer, initialState);
  const finalExecuteTransition = executeTransition(history);

  return {
    ...store,
    dispatch(action) {
      const { meta } = action;
      const transitionMetaFunc = meta ?
        (meta.transition || catchAllHandler) :
        catchAllHandler;

      const prevState = transitionMetaFunc && store.getState();

      store.dispatch(action);

      const nextState = transitionMetaFunc && store.getState();

      const transitionData = transitionMetaFunc && (
        transitionMetaFunc(prevState, nextState, action)
      );

      if (transitionData) {
        if (typeof transitionData.then === 'function') {
          transitionData
          .then(finalExecuteTransition)
          .catch(finalExecuteTransition);
        } else {
          finalExecuteTransition(transitionData);
        }
      }

      return action;
    },
  };
};

Support returning a promise from the transition handler so we can support delayed transitions


// Return Promise
const imageMerge = (sources = []) => new Promise(resolve => {
	// Load sources
	const images = sources.map(src => new Promise(resolve => {
		const img = new Image();
		img.onload = () => resolve(img);
		img.src = src;
	}));

	// Create canvas
	const canvas = document.createElement('canvas');
	const ctx = canvas.getContext('2d');

	// When sources have loaded
	Promise.all(images)
		.then(images => {
			// Draw images to canvas
			images.forEach(img => ctx.drawImage(img, 0, 0));

			// Resolve data uri
			resolve(canvas.toDataURL());
		});
});

module.exports = imageMerge;

// Return Promise
const imageMerge = (sources = []) => new Promise(resolve => {
	// Load sources
	const images = sources.map(src => new Promise(resolve => {
		const img = new Image();
		img.onload = () => resolve(img);
		img.src = src;
	}));

	// Create canvas
	const canvas = document.createElement('canvas');
	const ctx = canvas.getContext('2d');

	// When sources have loaded
	Promise.all(images)
		.then(images => {
			// Set canvas dimensions
			canvas.width = Math.max(images.map(img => img.width));
			canvas.height = Math.max(images.map(img => img.height));

			// Draw images to canvas
			images.forEach(img => ctx.drawImage(img, 0, 0));

			// Resolve data uri
			resolve(canvas.toDataURL());
		});
});

module.exports = imageMerge;

Set canvas size to width/height of widest/highest image


import React from 'react';
import ReactDOM from 'react-dom';

// This is a class of a component
// To create an instance of this class use <App />
const App = function() {
  return
Hi
;
}

// Render App inside an element with class name 'container'
ReactDOM.render(<App />, document.querySelector('.container'));

import React from 'react';
import ReactDOM from 'react-dom';

// This is a class of a component
// To create an instance of this class use <App />
const App = () => {
  return
Hi
;
}

// Render App inside an element with class name 'container'
ReactDOM.render(<App />, document.querySelector('.container'));

Use ES6 syntax for function declarative.


'use strict';

export default class Input {
	constructor(element, canvas) {
		this.pressedKeys = {};
		this.framePressedKeys = {};
		this.previousPressedKeys = {};
		element.addEventListener('keydown', (e) => {
			this.pressedKeys[e.keyCode] = true;
		});
		element.addEventListener('keyup', (e) => {
			this.pressedKeys[e.keyCode] = false;
		});
		element.addEventListener('blur', (e) => {
			this.pressedKeys = {};
		});
		canvas.oncontextmenu = (e) => {
			e.preventDefault();
		};
	}
	update() {
		for (var key in this.framePressedKeys) {
			this.previousPressedKeys[key] = this.framePressedKeys[key];
		}
		for (var key in this.pressedKeys) {
			this.framePressedKeys[key] = this.pressedKeys[key];
		}
	}
	wasKeyPressed(key) {
		return this.framePressedKeys[key] && !this.previousPressedKeys[key];
	}
	wasKeyReleased(key) {
		return !this.framePressedKeys[key] && this.previousPressedKeys[key];
	}
	isKeyDown(key) {
		return this.pressedKeys[key] === true;
	}
}

'use strict';

export default class Input {
	constructor(element, canvas) {
		this.pressedKeys = {};
		this.framePressedKeys = {};
		this.previousPressedKeys = {};
		element.addEventListener('keydown', (e) => {
			this.pressedKeys[e.keyCode] = true;
		});
		element.addEventListener('keyup', (e) => {
			this.pressedKeys[e.keyCode] = false;
		});
		element.addEventListener('blur', (e) => {
			this.pressedKeys = {};
		});
		canvas.oncontextmenu = (e) => {
			e.preventDefault();
		};
	}
	update() {
		for (var key in this.framePressedKeys) {
			this.previousPressedKeys[key] = this.framePressedKeys[key];
		}
		for (var key in this.pressedKeys) {
			this.framePressedKeys[key] = this.pressedKeys[key];
		}
	}
	wasKeyPressed(key) {
		return this.framePressedKeys[key] && !this.previousPressedKeys[key];
	}
	wasKeyReleased(key) {
		return !this.framePressedKeys[key] && this.previousPressedKeys[key];
	}
	wasAnyKeyReleased() {
		for (var key in this.previousPressedKeys) {
			if (!this.framePressedKeys[key] && this.previousPressedKeys[key]) {
				return true;
			}
		}
		return false;
	}
	isKeyDown(key) {
		return this.pressedKeys[key] === true;
	}
}

Add Input.wasAnyKeyReleased() to check if any key was pressed


var crypto = require('crypto')
  , qs = require('querystring')
  ;

function sha1 (key, body) {
  return crypto.createHmac('sha1', key).update(body).digest('base64')
}

function rfc3986 (str) {
  return encodeURIComponent(str)
    .replace('!','%21')
    .replace('*','%2A')
    .replace('(','%28')
    .replace(')','%29')
    .replace("'",'%27')
    ;
}

function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret, body) {
  // adapted from https://dev.twitter.com/docs/auth/oauth
  var base =
    (httpMethod || 'GET') + "&" +
    encodeURIComponent(  base_uri ) + "&" +
    Object.keys(params).sort().map(function (i) {
      // big WTF here with the escape + encoding but it's what twitter wants
      return escape(rfc3986(i)) + "%3D" + escape(rfc3986(params[i]))
    }).join("%26")
  var key = consumer_secret + '&'
  if (token_secret) key += token_secret
  return sha1(key, base)
}

exports.hmacsign = hmacsign
exports.rfc3986 = rfc3986

var crypto = require('crypto')
  , qs = require('querystring')
  ;

function sha1 (key, body) {
  return crypto.createHmac('sha1', key).update(body).digest('base64')
}

function rfc3986 (str) {
  return encodeURIComponent(str)
    .replace(/!/g,'%21')
    .replace(/\*/g,'%2A')
    .replace(/\(/g,'%28')
    .replace(/\)/g,'%29')
    .replace(/'/g,'%27')
    ;
}

function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret, body) {
  // adapted from https://dev.twitter.com/docs/auth/oauth
  var base =
    (httpMethod || 'GET') + "&" +
    encodeURIComponent(  base_uri ) + "&" +
    Object.keys(params).sort().map(function (i) {
      // big WTF here with the escape + encoding but it's what twitter wants
      return escape(rfc3986(i)) + "%3D" + escape(rfc3986(params[i]))
    }).join("%26")
  var key = consumer_secret + '&'
  if (token_secret) key += token_secret
  return sha1(key, base)
}

exports.hmacsign = hmacsign
exports.rfc3986 = rfc3986

Replace all occurrences of special chars in RFC3986


/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * 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
 *
 *      http://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.
 */

import {checkData, validateDataExists, writeScript} from '../src/3p';

/**
 * @param {!Window} global
 * @param {!Object} data
 */
export function openadstream(global, data) {
  //all available fields
  checkData(data, ['adhost', 'sitepage', 'pos', 'query']);
  //required fields
  validateDataExists(data, ['adhost', 'sitepage', 'pos']);
  let url = 'https://' + encodeURIComponent(data.adhost) + '/3/' + encodeURIComponent(data.sitepage) + '/1' + new String(Math.random()).substring(2, 11) + '@' + data.pos;
  if (data.query) {
    url = url + '?' + data.query;
  }
  writeScript(global, url);

}

/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * 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
 *
 *      http://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.
 */

import {checkData, validateDataExists, writeScript} from '../src/3p';

/**
 * @param {!Window} global
 * @param {!Object} data
 */
export function openadstream(global, data) {
  //all available fields
  checkData(data, ['adhost', 'sitepage', 'pos', 'query']);
  //required fields
  validateDataExists(data, ['adhost', 'sitepage', 'pos']);
  let url = 'https://' + encodeURIComponent(data.adhost) + '/3/' + data.sitepage + '/1' + String(Math.random()).substring(2, 11) + '@' + data.pos;
  if (data.query) {
    url = url + '?' + data.query;
  }
  writeScript(global, url);

}

Remove encoding which is breaking forward slash `/` in URL constructions


describe("Buttercup.vendor", function() {

	"use strict";

	describe("iocane", function() {

        it("derives the correct password", function(done) {
            window.Buttercup.vendor.iocane.derivation.deriveFromPassword("p455", "salt", 30000)
                .then(function(hash) {
                    return hash.key.toString("hex");
                })
                .then(function(hash) {
                    expect(hash).toBe("3206e36d28a3139def5037d1a0d25b4eb12cd8a70f715517206e7f07f8f8dd2b");
                    (done)();
                })
                .catch(function(err) {
                    console.error(err);
                });
        });

    });

});

describe("Buttercup.vendor", function() {

	"use strict";

	describe("iocane", function() {

        it("derives the correct password", function(done) {
            window.Buttercup.vendor.iocane.derivation.deriveFromPassword("p455", "salt", 30000)
                .then(function(hash) {
                    return hash.key.toString("hex");
                })
                .then(function(hash) {
                    expect(hash).toBe("3206e36d28a3139def5037d1a0d25b4eb12cd8a70f715517206e7f07f8f8dd2b");
                    (done)();
                })
                .catch(function(err) {
                    console.error(err);
                });
        });

        it("handles a large amount of rounds", function(done) {
            window.Buttercup.vendor.iocane.derivation.deriveFromPassword("some-password", "123salt", 250000)
                .then((hash) => hash.key.toString("hex"))
                .then(function(hash) {
                    expect(hash).toBe("c6aabb1f7cf5a74f39d74a72e4b5708407de63177deddf86f930dc0197100acd");
                })
                .then(done)
                .catch(function(err) {
                    console.error(err);
                });
        })

    });

});

Test for a large amount of PBKDF2 rounds


const Request = require('request');
const env2 = require('env2');
const HapiCookie = require('hapi-auth-cookie');

env2('./config.env');

module.exports = {
  path: '/welcome',
  method: 'GET',
  handler: (req, rep) => {
    const accessUrl = `https://github.com/login/oauth/access_token`;
    Request.post({
      headers: {
        accept: 'application/json'
      },
      url: accessUrl,
      form: {
        client_id: process.env.CLIENT_ID,
        client_secret: process.env.CLIENT_SECRET,
        code: req.query.code
      }
    }, (err, res, body) => {
      if (err) throw err;
      let parsed = JSON.parse(body);
      const userToken = {
        access_token: parsed.access_token
      };
      Request.get({
        headers: {
          'User-Agent': 'GitPom',
          // as recommended by the API documentation
          Accept: `application/vnd.github.v3+json`,
          Authorization: `token ${userToken.access_token}`
        },
        url: `https://api.github.com/user`
      }, (err, res, body) => {
        if (err) throw err;
        parsed = JSON.parse(body);
        const userDetails = {
          userName: parsed.login,
          avatarUrl: parsed.avatar_url
        };
        // set the cookie containing the token, the username and the avatar url
        req.cookieAuth.set(Object.assign(userToken, userDetails));
        rep.redirect('/');
      });
    });
  }
};

const Request = require('request');
const env2 = require('env2');
const HapiCookie = require('hapi-auth-cookie');

env2('./config.env');

module.exports = {
  path: '/welcome',
  method: 'GET',
  handler: (req, rep) => {
    const accessUrl = `https://github.com/login/oauth/access_token`;
    Request.post({
      headers: {
        // as recommended by the API documentation
        Accept: `application/vnd.github.v3+json`
      },
      url: accessUrl,
      form: {
        client_id: process.env.CLIENT_ID,
        client_secret: process.env.CLIENT_SECRET,
        code: req.query.code
      }
    }, (err, res, body) => {
      if (err) throw err;
      let parsed = JSON.parse(body);
      const userToken = {
        access_token: parsed.access_token
      };
      Request.get({
        headers: {
          'User-Agent': 'GitPom',
          // as recommended by the API documentation
          Accept: `application/vnd.github.v3+json`,
          Authorization: `token ${userToken.access_token}`
        },
        url: `https://api.github.com/user`
      }, (err, res, body) => {
        if (err) throw err;
        parsed = JSON.parse(body);
        const userDetails = {
          userName: parsed.login,
          avatarUrl: parsed.avatar_url
        };
        // set the cookie containing the token, the username and the avatar url
        req.cookieAuth.set(Object.assign(userToken, userDetails));
        rep.redirect('/');
      });
    });
  }
};

Update accept header according to Github API documentation


describe('Bot class', function () {

  beforeEach(function () {
    s.util.loadModels = function(options) {
      var toLoad = options.models.length;

      // Hold models
      var models = {};

      var loaded = function(name, geometry, materials) {
        // Store model and materials
        models[name] = {
          geometry: geometry,
          materials: materials
        };

        // Track progress
        toLoad--;
        if (toLoad === 0) {
          console.log('Models loaded!');
          if (typeof options.complete === 'function')
            options.complete(models);
        }
        else {
          var pct = (options.models.length-toLoad)/options.models.length*100;

          console.log('Loading models: '+pct.toFixed(0)+'%');
          if (typeof options.progress === 'function')
            options.progress(pct, models[name]);
        }
      };

      options.models.forEach(function(name, index) {
        s.loader.load('/base/tests/mock/' + name + '.json', loaded.bind(null, name));
      });
    };

    s.init();

    var bot = new s.Bot({
      game: s.game,
      name: 'bot 1',
      shipClass: 'human_ship_heavy',
      position: [22498, -25902, 24976],
      rotation: [0, Math.PI / 2, 0],
      alliance: 'enemy'
    });
  });

  it('should create new Bot instance', function () {
    expect(true).to.equal(true);
  });
});

describe('Bot class', function () {
  it('should create new Bot instance', function (cb) {
      setTimeout(function () {
        var bot = new s.Bot({
          game: s.game,
          name: 'bot 1',
          shipClass: 'human_ship_heavy',
          position: [22498, -25902, 24976],
          rotation: [0, Math.PI / 2, 0],
          alliance: 'enemy'
        });

        expect(bot).to.be.an('object');

        cb();
      }, 1000);
  });
});

Create base structure to start testing bot class


import sequelize from '../config/sequelize';
import DataTypes from 'sequelize';
import Officer from './officer';

export default sequelize.define('committees', {
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    primaryKey: true,
  },
  description: {
    type: DataTypes.TEXT,
    allowNull: true,
  },
}, {
  scopes: {
    name(name) {
      return { where: { name } };
    },
    active(date) {
      return {
        include: [{
          model: Officer.scope({ method: ['active', date] }),
        }],
      };
    },
  },
});

import sequelize from '../config/sequelize';
import DataTypes from 'sequelize';
import paginate from '../helpers/paginate';
import Officer from './officer';

export default sequelize.define('committees', {
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    primaryKey: true,
  },
  description: {
    type: DataTypes.TEXT,
    allowNull: true,
  },
}, {
  scopes: {
    name(name) {
      return { where: { name } };
    },
    active(date) {
      return {
        include: [{
          model: Officer.scope({ method: ['active', date] }),
        }],
      };
    },
    paginate,
  },
});

Revert "Testing out removing pagination from Committees"


/*
 * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-thinking-things
 *
 * iotagent-thinking-things is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-thinking-things is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-thinking-things.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[iot_support@tid.es]
 */

'use strict';

var responseGenerator = require('./responseGenerator');

function updateContextHandler(id, type, attributes, callback) {
    responseGenerator.continueSynchOperation(id, attributes);

    callback(null, {
        type: type,
        isPattern: false,
        id: id,
        attributes: attributes
    });
}

function queryContextHandler(id, type, attributes, callback) {
    callback(null, {});
}

exports.updateContextHandler = updateContextHandler;
exports.queryContextHandler = queryContextHandler;

/*
 * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-thinking-things
 *
 * iotagent-thinking-things is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-thinking-things is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-thinking-things.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[iot_support@tid.es]
 */

'use strict';

var responseGenerator = require('./responseGenerator');

function updateContextHandler(id, type, attributes, callback) {
    responseGenerator.continueSynchOperation(id, attributes);

    callback(null, {
        type: type,
        isPattern: false,
        id: id,
        attributes: attributes
    });
}

function queryContextHandler(id, type, attributes, callback) {
    callback(null, []);
}

exports.updateContextHandler = updateContextHandler;
exports.queryContextHandler = queryContextHandler;

FIX Return array instead of object in Query Context handlers


/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'fireplace-app',
    environment: environment,
    baseURL: '/fireplace-app',
    locationType: 'auto',
    contentSecurityPolicy: {'img-src': "'self' " +
                                       "m.fmi.fi "},
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {
    ENV.baseURL = '/fireplace-app/';
  }

  return ENV;
};

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'fireplace-app',
    environment: environment,
    baseURL: '/fireplace-app',
    locationType: 'auto',
    contentSecurityPolicy: {'img-src': "'self' " +
                                       "m.fmi.fi "},
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {
    ENV.baseURL = '/fireplace-app/';
    ENV.locationType = 'auto';
  }

  return ENV;
};

Adjust the production build parameter inorder to access all available routes


'use strict';

require('../helper');

const server = require('../../app/server')();
const assert = require('../support/assert');
const qs = require('querystring');

const QUERY = `SELECT 14 as foo`;
const API_KEY = 1234;
const BODY_PAYLOAD = {
    q: QUERY,
    api_key: API_KEY
};

describe.only('Handle query middleware', function() {
    ['GET', 'POST'].forEach(method => {
        it(`${method} query`, function(done) {
            assert.response(server,
                {
                    method,
                    url: '/api/v1/sql?' + qs.stringify(BODY_PAYLOAD),
                    headers: {
                        host: 'vizzuality.cartodb.com'
                    }
                },
                { statusCode: 200 },
                function(err, res) {
                    assert.ok(!err);

                    const response = JSON.parse(res.body);
                    assert.equal(response.rows.length, 1);
                    assert.equal(response.rows[0].foo, 14);

                    done();
                }
            );
        });
    });
});

'use strict';

require('../helper');

const server = require('../../app/server')();
const assert = require('../support/assert');
const qs = require('querystring');

const QUERY = `SELECT 14 as foo`;
const API_KEY = 1234;

describe.only('Handle query middleware', function() {
    ['GET', 'POST'].forEach(method => {
        it(`${method} without query fails`, function(done) {
            assert.response(server,
                {
                    method,
                    url: '/api/v1/sql?' + qs.stringify({
                        api_key: API_KEY
                    }),
                    headers: {
                        host: 'vizzuality.cartodb.com'
                    }
                },
                { statusCode: 400 },
                function(err, res) {
                    assert.ok(!err);

                    const response = JSON.parse(res.body);
                    assert.deepEqual(response, { error: ["You must indicate a sql query"] });

                    done();
                }
            );
        });

        it(`${method} query`, function(done) {
            assert.response(server,
                {
                    method,
                    url: '/api/v1/sql?' + qs.stringify({
                        q: QUERY,
                        api_key: API_KEY
                    }),
                    headers: {
                        host: 'vizzuality.cartodb.com'
                    }
                },
                { statusCode: 200 },
                function(err, res) {
                    assert.ok(!err);

                    const response = JSON.parse(res.body);
                    assert.equal(response.rows.length, 1);
                    assert.equal(response.rows[0].foo, 14);

                    done();
                }
            );
        });
    });
});

Handle query middleware failing tests


import test from 'ava';
import Keyv from '../';

test('Keyv is a class', t => {
	t.is(typeof Keyv, 'function');
	t.throws(() => Keyv()); // eslint-disable-line new-cap
	t.notThrows(() => new Keyv());
});

test('Keyv accepts storage adapters', async t => {
	const store = new Map();
	const keyv = new Keyv({ store });
	await keyv.set('foo', 'bar');
	t.is(await keyv.get('foo'), 'bar');
	t.true(store.has('foo'));
});

import test from 'ava';
import delay from 'delay';
import Keyv from '../';

test('Keyv is a class', t => {
	t.is(typeof Keyv, 'function');
	t.throws(() => Keyv()); // eslint-disable-line new-cap
	t.notThrows(() => new Keyv());
});

test('Keyv accepts storage adapters', async t => {
	const store = new Map();
	const keyv = new Keyv({ store });
	await keyv.set('foo', 'bar');
	t.is(await keyv.get('foo'), 'bar');
	t.true(store.has('foo'));
});

test('Keyv hands tll functionality over to ttl supporting stores', async t => {
	const store = new Map();
	store.ttlSupport = true;
	const keyv = new Keyv({ store });
	await keyv.set('foo', 'bar', 100);
	t.is(await keyv.get('foo'), 'bar');
	t.is(store.get('foo'), 'bar');
	await delay(100);
	t.is(await keyv.get('foo'), 'bar');
});

Test Keyv hands tll functionality over to ttl supporting stores


'use strict';

user_pref('browser.console.showInPanel', true);
user_pref('browser.dom.window.dump.enabled', true);
user_pref('browser.firstrun.show.localepicker', false);
user_pref('browser.firstrun.show.uidiscovery', false);
user_pref('dom.allow_scripts_to_close_windows', true);
user_pref('dom.disable_open_during_load', false);
user_pref('dom.max_script_run_time', 0); // no slow script dialogs
user_pref('dom.max_chrome_script_run_time', 0);
user_pref('dom.popup_maximum', -1);
user_pref('dom.send_after_paint_to_content', true);
user_pref('dom.successive_dialog_time_limit', 0);
user_pref('security.warn_submit_insecure', false);
user_pref('browser.shell.checkDefaultBrowser', false);
user_pref('shell.checkDefaultClient', false);
user_pref('browser.warnOnQuit', false);
user_pref('accessibility.typeaheadfind.autostart', false);
user_pref('javascript.options.showInConsole', true);
user_pref('devtools.errorconsole.enabled', true);
user_pref('layout.debug.enable_data_xbl', true);
user_pref('browser.EULA.override', true);
user_pref('javascript.options.tracejit.content', true);
user_pref('javascript.options.methodjit.content', true);
user_pref('javascript.options.jitprofiling.content', true);
user_pref('javascript.options.methodjit_always', false);
user_pref('gfx.color_management.force_srgb', true);
user_pref('network.manage-offline-status', false);
user_pref('test.mousescroll', true);
user_pref('network.http.prompt-temp-redirect', false);
user_pref('media.cache_size', 100);
user_pref('security.warn_viewing_mixed', false);
user_pref('app.update.enabled', false);
user_pref('browser.panorama.experienced_first_run', true); // Assume experienced
user_pref('dom.w3c_touch_events.enabled', true);
user_pref('extensions.checkCompatibility', false);
user_pref('extensions.installDistroAddons', false); // prevent testpilot etc
user_pref('browser.safebrowsing.enable', false); // prevent traffic to google servers
user_pref('toolkit.telemetry.prompted', true); // prevent telemetry banner
user_pref('toolkit.telemetry.enabled', false);
user_pref('browser.tabs.remote.autostart', false);
user_pref('browser.tabs.remote.autostart.1', false);
user_pref('browser.tabs.remote.autostart.2', false);

Remove 'use strict'; causing failure and unused prefs.


var path = require('path');
var webpack = require('webpack');
var _ = require('lodash');

module.exports = function(options) {
  var config = {
    entry: [
      './src/js/main.js'
    ],
    output: {
      path: path.join(__dirname, 'build'),
      filename: 'app.js',
      publicPath: '/'
    },
    plugins: [
      new webpack.optimize.UglifyJsPlugin(),
      new webpack.optimize.DedupePlugin()
    ],
    resolve: {
      extensions: ['', '.js']
    },
    module: {
      loaders: [
        {
          test: /\.js$/,
          loaders: ['babel?stage=0'],
          include: path.join(__dirname, 'src/js')
        },
      ]
    },
  };

  if (options.environment === 'dev') {
    config.devtool = 'source-map';
    Array.prototype.unshift.call(
      config.entry,
      'webpack-dev-server/client?http://0.0.0.0:8000',
      'webpack/hot/only-dev-server'
    );
    config.plugins = [
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NoErrorsPlugin()
    ];
    config.module.loaders[0].loaders.unshift('react-hot');
  }

  config.module.loaders.unshift({
    test: require.resolve("react"),
    loader: "expose?React"
  });

  return config;
};

var path = require('path');
var webpack = require('webpack');
var _ = require('lodash');

module.exports = function(options) {
  var config = {
    entry: [
      './src/js/main.js'
    ],
    output: {
      path: path.join(__dirname, 'build'),
      filename: 'app.js',
      publicPath: ''
    },
    plugins: [
      new webpack.optimize.UglifyJsPlugin(),
      new webpack.optimize.DedupePlugin()
    ],
    resolve: {
      extensions: ['', '.js']
    },
    module: {
      loaders: [
        {
          test: /\.js$/,
          loaders: ['babel?stage=0'],
          include: path.join(__dirname, 'src/js')
        },
      ]
    },
  };

  if (options.environment === 'dev') {
    config.devtool = 'source-map';
    Array.prototype.unshift.call(
      config.entry,
      'webpack-dev-server/client?http://0.0.0.0:8000',
      'webpack/hot/only-dev-server'
    );
    config.plugins = [
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NoErrorsPlugin()
    ];
    config.module.loaders[0].loaders.unshift('react-hot');
  }

  config.module.loaders.unshift({
    test: require.resolve("react"),
    loader: "expose?React"
  });

  return config;
};

Fix async loading from subdirectory


"use strict";

var db = require('../../models');
var config = require('../../config.js');
var authHelper = require('../../lib/auth-helper');

module.exports = function (app, options) {

  app.get('/user/tokens',
    authHelper.ensureAuthenticated,
    function(req, res) {
      db.ServiceAccessToken
        .findAll({include: [db.ServiceProvider]})
        .complete(function(err, tokens){
          if(err) {
            res.send(500);
            return;
          }
          res.render('./user/token_list.ejs', {'tokens': tokens});
        });
    });
};

"use strict";

var db         = require('../../models');
var config     = require('../../config.js');
var authHelper = require('../../lib/auth-helper');

module.exports = function (app, options) {
  app.get('/user/tokens', authHelper.ensureAuthenticated, function(req, res, next) {
    db.ServiceAccessToken
      .findAll({ include: [db.ServiceProvider] })
      .complete(function(err, tokens) {
        if (err) {
          next(err);
          return;
        }

        res.render('./user/token_list.ejs', { tokens: tokens });
      });
    });
};

Handle errors by passing Error object to next() function


"use strict";

describe('Footer component', () => {
    let element = undefined;
    let $rootScope = undefined;
    let $compile = undefined;

    let mod = angular.module('tests.footer', []).service('Authentication', function () {
        this.check = false;
        this.attempt = credentials => {
            if (credentials.username == 'test' && credentials.password == 'test') {
                this.check = true;
            }
        };
        this.status = () => this.check;
    });
    beforeEach(angular.mock.module(mod.name));

    beforeEach(inject((_$rootScope_, _$compile_) => {
        $rootScope = _$rootScope_;
        $compile = _$compile_;
    }));

    describe('Footer while not logged in', () => {
        it('should not display a logout button', () => {
            let tpl = angular.element('<monad-footer></monad-footer>');
            element = $compile(tpl)($rootScope);
            $rootScope.$digest();
            expect(element.find('#logout').length).toBe(0);
        });
    });
});

"use strict";

describe('Footer component', () => {
    let element = undefined;
    let $rootScope = undefined;
    let $compile = undefined;

    let mod = angular.module('tests.footer', []).service('Authentication', function () {
        this.check = false;
        this.attempt = credentials => {
            if (credentials.username == 'test' && credentials.password == 'test') {
                this.check = true;
            }
        };
        this.status = () => this.check;
    });
    beforeEach(angular.mock.module(mod.name));

    beforeEach(inject((_$rootScope_, _$compile_) => {
        $rootScope = _$rootScope_;
        $compile = _$compile_;
    }));

    describe('Footer while not logged in', () => {
        it('should only display one li', () => {
            let tpl = angular.element('<monad-footer></monad-footer>');
            element = $compile(tpl)($rootScope);
            $rootScope.$digest();
            expect(element.find('li').length).toBe(1);
        });
    });
});

Check for amount of li's


/**
 * Returns the bounding box of the specified text
 * @param {string} text - specifies the text
 * @returns {Array<number>}
 */
export const textBoundingBox = (text) => {
    return [text.length * 8, 17]; // Inconsolata font prediction
};

/**
 * Returns the area covered by the specified render nodes
 * @param {Array<RenderNode>} renderNodes - specifies the render nodes
 * @returns {Array<Array<number>>|null}
 */
export const renderNodesBoundingBox = (renderNodes) => {
    if (renderNodes.length === 0) {
        return null;
    }
    return renderNodes.reduce((a, rn) => [
        [Math.min(a[0][0], rn.position[0]), Math.min(a[0][1], rn.position[1])],
        [Math.max(a[1][0], rn.position[0] + rn.size[0]), Math.max(a[1][1], rn.position[1] + rn.size[1])]
    ], [[Infinity, Infinity], [-Infinity, -Infinity]]);
};

/**
 * @param {Array<RenderPoint>} renderPoints - lol
 * @returns {Array<number>|null}
 */
export const renderPointsBoundingBox = (renderPoints) => {
    if (renderPoints.length === 0) {
        return null;
    }
    return renderPoints.reduce((a, rp) => [Math.max(a[0], rp.size[0]), Math.max(a[1], rp.size[1])], [0, 0]);
};

/**
 * Returns the bounding box of the specified text
 * @param {string} text - specifies the text
 * @returns {Array<number>}
 */
export const textBoundingBox = (text) => {
    return [text.length * 9.6, 18]; // OxygenSans font prediction
};

/**
 * Returns the area covered by the specified render nodes
 * @param {Array<RenderNode>} renderNodes - specifies the render nodes
 * @returns {Array<Array<number>>|null}
 */
export const renderNodesBoundingBox = (renderNodes) => {
    if (renderNodes.length === 0) {
        return null;
    }
    return renderNodes.reduce((a, rn) => [
        [Math.min(a[0][0], rn.position[0]), Math.min(a[0][1], rn.position[1])],
        [Math.max(a[1][0], rn.position[0] + rn.size[0]), Math.max(a[1][1], rn.position[1] + rn.size[1])]
    ], [[Infinity, Infinity], [-Infinity, -Infinity]]);
};

/**
 * @param {Array<RenderPoint>} renderPoints - lol
 * @returns {Array<number>|null}
 */
export const renderPointsBoundingBox = (renderPoints) => {
    if (renderPoints.length === 0) {
        return null;
    }
    return renderPoints.reduce((a, rp) => [Math.max(a[0], rp.size[0]), Math.max(a[1], rp.size[1])], [0, 0]);
};

Update text bounding box calculation method


'use strict';

var d = require('d')

  , create = Object.create, setPrototypeOf = Object.setPrototypeOf;

module.exports = function () {
	var set, iterator, result, SubSet;
	if (typeof Set !== 'function') return false;
	if (String(Set.prototype) !== '[object Set]') return false;
	set = new Set(['raz', 'dwa', 'trzy']);
	if (set.size !== 3) return false;
	if (typeof set.add !== 'function') return false;
	if (typeof set.clear !== 'function') return false;
	if (typeof set.delete !== 'function') return false;
	if (typeof set.entries !== 'function') return false;
	if (typeof set.forEach !== 'function') return false;
	if (typeof set.has !== 'function') return false;
	if (typeof set.keys !== 'function') return false;
	if (typeof set.values !== 'function') return false;

	iterator = set.values();
	result = iterator.next();
	if (result.done !== false) return false;
	if (result.value !== 'raz') return false;

	if (!set) return false;
	if (set.size !== 3) return false;

	// Extendable
	if (setPrototypeOf) {
		SubSet = function () { Set.apply(this, arguments); };
		setPrototypeOf(SubSet, Set);
		SubSet.prototype = create(Set.prototype, { constructor: d(SubSet) });
		try { set = new SubSet([1, 2, 3]); } catch (e) { return false; }
		try {
			if (set.size !== 3) return false;
		} catch (e) { return false; }
	}
	return true;
};

'use strict';

module.exports = function () {
	var set, iterator, result;
	if (typeof Set !== 'function') return false;
	if (String(Set.prototype) !== '[object Set]') return false;
	set = new Set(['raz', 'dwa', 'trzy']);
	if (set.size !== 3) return false;
	if (typeof set.add !== 'function') return false;
	if (typeof set.clear !== 'function') return false;
	if (typeof set.delete !== 'function') return false;
	if (typeof set.entries !== 'function') return false;
	if (typeof set.forEach !== 'function') return false;
	if (typeof set.has !== 'function') return false;
	if (typeof set.keys !== 'function') return false;
	if (typeof set.values !== 'function') return false;

	iterator = set.values();
	result = iterator.next();
	if (result.done !== false) return false;
	if (result.value !== 'raz') return false;

	if (!set) return false;
	if (set.size !== 3) return false;

	return true;
};

Remove invalid class extension test


'use strict';

module.exports = {
  app: ['./app'],
  admin: ['<%= project.app %>/client/admin'],
  srcjs: ['<%= project.admin %>/js/'],
  sass: ['<%= project.admin %>/compass/sass'],
  public: ['public'],
  css: ['<%= project.public %>/css'],
  js: ['<%= project.public %>/js'],
  font: ['../lib/bootstrap-sass/assets/fonts/'],
  uglify: ['build/uglify']
};

'use strict';

module.exports = {
  app: ['./app'],
  admin: ['<%= project.app %>/client/admin'],
  srcjs: ['<%= project.admin %>/js/'],
  sass: ['<%= project.admin %>/compass/sass'],
  public: ['public'],
  css: ['<%= project.public %>/css'],
  js: ['<%= project.public %>/js'],
  font: ['../../lib/bootstrap-sass/assets/fonts/'],
  uglify: ['build/uglify']
};

Modify Font path for grunt


// Continuous delivery server
const { spawn } = require('child_process')
const { resolve } = require('path')
const { createServer } = require('http')
const { urlencoded } = require('body-parser')

const hostname = '127.0.0.1'
const port = 80
const server = createServer((req, res) => {
  const { headers, method, url } = req

  // When a successful build has happened, kill the process, triggering a restart
  if (req.method === 'POST' && req.url === '/webhook') {
    // Send response
    res.statusCode = 200
    res.end()

    let body = []
    req
      .on('error', err => {
        console.error(err)
      })
      .on('data', chunk => {
        body.push(chunk)
      })
      .on('end', () => {
        req.body = Buffer.concat(body).toString()
        const data = urlencoded({ extended: true })(req)
        console.log(data)
      })
  }

  res.statusCode = 404
  res.end()
}).listen(port)

// Continuous delivery server
const { spawn } = require('child_process')
const { resolve } = require('path')
const { createServer } = require('http')
const { urlencoded } = require('body-parser')

const hostname = '127.0.0.1'
const port = 80
const server = createServer((req, res) => {
  const { headers, method, url } = req

  // When a successful build has happened, kill the process, triggering a restart
  if (req.method === 'POST' && req.url === '/webhook') {
    // Send response
    res.statusCode = 200
    res.end()

    let body = []
    req
      .on('error', err => {
        console.error(err)
      })
      .on('data', chunk => {
        body.push(chunk)
      })
      .on('end', () => {
        req.body = Buffer.concat(body).toString()
        const data = urlencoded({ extended: true })(req)
        console.log(req.body.payload)
        if (req.body.payload) {
          const passed = req.body.payload.state == 'passed'
          const master = req.body.payload.branch == 'master'
          process.exit(0)
        }
      })
  }

  res.statusCode = 404
  res.end()
}).listen(port)

Add restart logic to CD server.


Requests = new Mongo.Collection('requests');

Meteor.methods({
  addRequest: function(requestAttributes) {
    check(Meteor.userId(), String);

    // TODO add more checks

    var request = _.extend(requestAttributes, {
      createdAt: new Date(),
      status: 'pending'
    });

    Requests.insert(request);
    createRequestNotification(request);
  }
});

hasRequestPending = function(userId, projectId) {
  return Requests.find({
    'user.userId': userId,
    'project.projectId': projectId }).count() === 0;
};

Add hasRequestPending to check if user has already requested to join a project


import JobStatus from 'girder_plugins/jobs/JobStatus';

JobStatus.registerStatus({
    WORKER_FETCHING_INPUT: {
        value: 820,
        text: 'Fetching input',
        icon: 'icon-download',
        color: '#89d2e2'
    },
    WORKER_CONVERTING_INPUT: {
        value: 821,
        text: 'Converting input',
        icon: 'icon-shuffle',
        color: '#92f5b5'
    },
    WORKER_CONVERTING_OUTPUT: {
        value: 822,
        text: 'Converting output',
        icon: 'icon-shuffle',
        color: '#92f5b5'
    },
    WORKER_PUSHING_OUTPUT: {
        value: 823,
        text: 'Pushing output',
        icon: 'icon-upload',
        color: '#89d2e2'
    },
    WORKER_CANCELING: {
        value: 824,
        text: 'Canceling',
        icon: 'icon-cancel',
        color: '#f89406'
    }
});

import JobStatus from 'girder_plugins/jobs/JobStatus';

JobStatus.registerStatus({
    WORKER_FETCHING_INPUT: {
        value: 820,
        text: 'Fetching input',
        icon: 'icon-download',
        color: '#89d2e2'
    },
    WORKER_CONVERTING_INPUT: {
        value: 821,
        text: 'Converting input',
        icon: 'icon-shuffle',
        color: '#92f5b5'
    },
    WORKER_CONVERTING_OUTPUT: {
        value: 822,
        text: 'Converting output',
        icon: 'icon-shuffle',
        color: '#92f5b5'
    },
    WORKER_PUSHING_OUTPUT: {
        value: 823,
        text: 'Pushing output',
        icon: 'icon-upload',
        color: '#89d2e2'
    },
    WORKER_CANCELING: {
        value: 824,
        text: 'Canceling',
        icon: 'icon-spin3 animate-spin',
        color: '#f89406'
    }
});

Change canceling icon to 'spinning orange'


Template.newPost.events({
  'submit': function () {
    "use strict";
    event.preventDefault();
    var title = $('form .title').val();
    var url =   $('form .url').val();
    Meteor.call('newPost', {
      title: title,
      url: url
    }, function (err) {
      if (err) {
        throw err;
      } else {
        document.location.href = '/';
      }
    });
  }
});

Template.newPost.events({
  'submit': function (event) {
    "use strict";
    event.preventDefault();
    var title = $('form .title').val();
    var url =   $('form .url').val();
    Meteor.call('newPost', {
      title: title,
      url: url
    }, function (err) {
      if (err) {
        throw err;
      } else {
        document.location.href = '/';
      }
    });
  }
});

Fix Firefox bug when creating a new post


const db = require('./postgresql');

const { VOTING_FINISHED } = require('../../common/actionTypes/issues');

const Question = db.sequelize.models.issue;

async function addIssue(issue) {
  return Question.create(issue);
}

function getConcludedIssues(genfors) {
  const id = genfors.id || genfors;
  return Question.findOne({ where:
    { id, deleted: false, active: false },
  });
}

const getIssueById = id => (
  Question.findOne({ _id: id })
);
function getActiveQuestion(genfors) {
  return Question.findOne({ genfors, active: true, deleted: false });
}

function endIssue(issue) {
  return Question.findByIdAndUpdate(issue,
    { active: false, status: VOTING_FINISHED },
    { new: true });
}

function deleteIssue(issue) {
  return Question.findByIdAndUpdate(issue,
    { active: false, deleted: true, status: VOTING_FINISHED },
    { new: true });
}

function updateIssue(issue, data, options) {
  return Question.findOneAndUpdate(issue, data, options);
}

module.exports = {
  addIssue,
  getActiveQuestion,
  getClosedQuestions: getConcludedIssues,
  getIssueById,
  getConcludedIssues,
  endIssue,
  deleteIssue,
  updateIssue,
};

const db = require('./postgresql');

const { VOTING_FINISHED } = require('../../common/actionTypes/issues');

const Question = db.sequelize.models.issue;

async function addIssue(issue) {
  return Question.create(issue);
}

function getConcludedIssues(genfors) {
  const id = genfors.id || genfors;
  return Question.findAll({ where:
    { genforsId: id, deleted: false, active: false },
  });
}

const getIssueById = id => (
  Question.findOne({ where: { id } })
);
function getActiveQuestion(genfors) {
  return Question.findOne({ where:
    { genforsId: genfors, active: true, deleted: false },
  });
}

async function updateIssue(issueOrId, data) {
  const id = issueOrId.id || issueOrId;
  const issue = await getIssueById(id);
  return Object.assign(issue, data).save();
}

async function deleteIssue(issue) {
  const id = issue.id || issue;
  return updateIssue(id,
    { active: false, deleted: true, status: VOTING_FINISHED },
    { new: true });
}

function endIssue(issue) {
  const id = issue.id || issue;
  return updateIssue(id,
    { active: false, status: VOTING_FINISHED },
    { new: true });
}

module.exports = {
  addIssue,
  getActiveQuestion,
  getClosedQuestions: getConcludedIssues,
  getIssueById,
  getConcludedIssues,
  endIssue,
  deleteIssue,
  updateIssue,
};

Update issue accessor functions for Sequelize


var pingPong = function(userNumber) {
  if ((userNumber % 3 === 0) || (userNumber % 5 === 0)) {
    return true;
  } else {
    return false;
  }
};

$(function() {
  $("form#game").submit(function(event) {

    event.preventDefault();
  });
});

Add beginning of code for user interface logic


'use strict';

/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */

describe('Real Racing Telemetry Client App', function() {

  describe('Car list view', function() {

    beforeEach(function() {
      browser.get('http://localhost:8000/app/index.html');
    });

    it('should filter the car list as a user types into the search box', function() {

      var carList = element.all(by.repeater('car in cars'));
      var query = element(by.model('query'));

      expect(carList.count()).toBe(4);

      query.sendKeys('ford');
      expect(carList.count()).toBe(2);

      query.clear();
      query.sendKeys('audi');
      expect(carList.count()).toBe(1);
    });
  });
});

'use strict';

/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */

describe('Real Racing Telemetry Client App', function() {

  describe('Car list view', function() {

    beforeEach(function() {
      browser.get('http://localhost:8000/app/index.html');
    });

    it('should filter the car list as a user types into the search box', function() {

      var carList = element.all(by.repeater('car in cars'));
      var query = element(by.model('query'));

      expect(carList.count()).toBe(4);

      query.sendKeys('ford');
      expect(carList.count()).toBe(2);

      query.clear();
      query.sendKeys('audi');
      expect(carList.count()).toBe(1);
    });

    it('should be possible to control car order via the drop down select box', function() {

      var carModelColumn = element.all(by.repeater('car in cars').column('car.model'));
      var query = element(by.model('query'));

      function getNames() {
        return carModelColumn.map(function(elm) {
          return elm.getText();
        });
      }
      query.sendKeys('f'); //let's narrow the dataset to make the test assertions shorter

      expect(getNames()).toEqual([
        "Focus RS",
        "Fusion",
        "LaFerrari"
      ]);

      element(by.model('orderProp')).element(by.css('option[value="manufacturer"]')).click();

      expect(getNames()).toEqual([
        "LaFerrari",
        "Focus RS",
        "Fusion"
      ]);
    });

  });
});

Test orderBy selection works, plus works with search box.


// ==UserScript==
// @name       Slickdeals Don't Track Me!
// @version    1.1
// @description  Replaces outgoing Slickdeals tracking links with direct links.
// @match      http://slickdeals.net/f/*
// @namespace https://github.com/gg/slickdeals-dont-track-me
// @author    Gregg Gajic <https://github.com/gg>
// @license    MIT
// ==/UserScript==

(function() {
  var getQueryStringParameters = function (url) {
    // See: http://stackoverflow.com/a/3855394
    return (function(a) {
        if (a == "") return {};
        var b = {};
        for (var i = 0; i < a.length; ++i)
        {
            var p=a[i].split('=', 2);
            if (p.length == 1)
                b[p[0]] = "";
            else
                b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
        }
        return b;
    })((url.split('?')[1] || '').split('&'));
  };

  var contentBody = document.getElementById('maincontent') || document.getElementById('posts');
  var aElements = contentBody.getElementsByTagName('a');
  for (var i = 0, a; a = aElements[i]; i++) {
    var queryParamters = getQueryStringParameters(a.href);
    var directUrl = queryParamters['u2'];
    if (directUrl) {
      a.href = directUrl;
      a.onclick = '';
    };
  }
})();

// ==UserScript==
// @name       Slickdeals Don't Track Me!
// @version    1.1
// @description  Replaces outgoing Slickdeals tracking links with direct links.
// @match      http://slickdeals.net/f/*
// @namespace https://github.com/gg/slickdeals-dont-track-me
// @author    Gregg Gajic <https://github.com/gg>
// @license    MIT
// ==/UserScript==

(function() {
  var getQueryStringParams = function (url) {
    // See: http://stackoverflow.com/a/3855394
    return (function(a) {
        if (a == "") return {};
        var b = {};
        for (var i = 0; i < a.length; ++i)
        {
            var p=a[i].split('=', 2);
            if (p.length == 1)
                b[p[0]] = "";
            else
                b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
        }
        return b;
    })((url.split('?')[1] || '').split('&'));
  };

  var contentBody = document.getElementById('maincontent') || document.getElementById('posts');
  var aElements = contentBody.getElementsByTagName('a');
  for (var i = 0, a; a = aElements[i]; i++) {
    var queryParams = getQueryStringParams(a.href);
    var directUrl = queryParams['u2'];
    if (directUrl) {
      a.href = directUrl;
      a.onclick = '';
    };
  }
})();

Use 'params' instead of 'parameters'


getBestName = function(result){
    var bestName = "";
    if(typeof result["scientificName"] !== "undefined"){
        bestName = result["scientificName"];
    }
    if(typeof result["vernacularNames"] !== "undefined" && result["vernacularNames"].length > 0){
        var preferred = false;
        $.each(result["vernacularNames"], function(key, value){
            if(value.language === "en"){
                if(typeof value["eol_preferred"] !== "undefined" && value["eol_preferred"]){
                    preferred = true;
                    bestName = value.vernacularName;
                }
                else if(!preferred){
                    bestName = value.vernacularName;
                }
            }
        });
    }
    return bestName;
}

getBestName = function(result){
    var bestName = "";
    if(typeof result["scientificName"] !== "undefined"){
        bestName = result["scientificName"];
    }
    if(typeof result["vernacularNames"] !== "undefined" && result["vernacularNames"].length > 0){
        var preferred = false;
        result["vernacularNames"].forEach(function(value){
            if(value.language === "en"){
                if(typeof value["eol_preferred"] !== "undefined" && value["eol_preferred"]){
                    preferred = true;
                    bestName = value.vernacularName;
                }
                else if(!preferred){
                    bestName = value.vernacularName;
                }
            }
        });
    }
    return bestName;
}

Rewrite getBestName to avoid jQuery


$(document).foundation()

context = new AudioContext()

// declare globals
var saw,
    volume = context.createGain()

// initialize volume
volume.gain.value = 0.1

function init() {
    saw = context.createOscillator()
    saw.type = 'sawtooth'
    saw.frequency.value = 440           // set the frequency to 440 HZ

    saw.connect(volume)
    volume.connect(context.destination)
}

function startSaw() {
    saw.start(0)
}

function stopSaw() {
    saw.stop()
}

// Gain input event changes level from 0 to 1
$('#gain').on('input',function(e){
    gain = $("#gain").val()
    volume.gain.value = gain
});

// test keypress 'a'
$(document).keydown(function(e) {
    if (e.which == 65) {
        startSaw()
    }
});

init()

$(document).foundation()

context = new AudioContext()

// declare globals
var note,
    volume = context.createGain()

// initialize volume
volume.gain.value = 0.1

function initNote(freq) {
    note = context.createOscillator()
    note.type = 'sawtooth'
    note.frequency.value = freq
    note.connect(volume)
    volume.connect(context.destination)
}

function startSaw() {
    initNote(440)
    note.start(0)
}

function stopSaw() {
    note.stop()
}

// Gain input event changes level from 0 to 1
$('#gain').on('input',function(e){
    gain = $("#gain").val()
    volume.gain.value = gain
});

// test keypress 'a'
$(document).keydown(function(e) {
    if (e.which == 65) {
        startSaw()
    }
});

Change init() to initNote() to handle each note


var SpotifyWebApi = require("../");

/*
 * This example shows how to search for a track. The endpoint is documented here:
 * https://developer.spotify.com/web-api/search-item/

 * Please note that this endpoint does not require authentication. However, using an access token
 * when making requests will give your application a higher rate limit.
 */

var spotifyApi = new SpotifyWebApi();

spotifyApi.searchTracks('Love')
  .then(function(data) {

    // Print some information about the results
    console.log('I got ' + data.tracks.total + ' results!');

    // Go through the first page of results
    var firstPage = data.tracks.items;
    console.log('The tracks in the first page are.. (popularity in parentheses)');

    /*
     * 0: All of Me (97)
     * 1: My Love (91)
     * 2: I Love This Life (78)
     * ...
     */
    firstPage.forEach(function(track, index) {
      console.log(index + ': ' + track.name + ' (' + track.popularity + ')');
    });

  }, function(err) {
    console.error(err);
  });

var SpotifyWebApi = require("../");

/*
 * This example shows how to search for a track. The endpoint is documented here:
 * https://developer.spotify.com/web-api/search-item/

 * Please note that this endpoint does not require authentication. However, using an access token
 * when making requests will give your application a higher rate limit.
 */

var spotifyApi = new SpotifyWebApi();

spotifyApi.searchTracks('Love', function(err, data) {
  if (err) {
    console.error('Something went wrong', err);
    return;
  }

  // Print some information about the results
  console.log('I got ' + data.tracks.total + ' results!');

  // Go through the first page of results
  var firstPage = data.tracks.items;
  console.log('The tracks in the first page are.. (popularity in parentheses)');

  /*
   * 0: All of Me (97)
   * 1: My Love (91)
   * 2: I Love This Life (78)
   * ...
   */
  firstPage.forEach(function(track, index) {
    console.log(index + ': ' + track.name + ' (' + track.popularity + ')');
  });
});

Update one of the examples to use callback


/*
    This file is generated and updated by Sencha Cmd. You can edit this file as
    needed for your application, but these edits will have to be merged by
    Sencha Cmd when upgrading.
*/

// DO NOT DELETE - this directive is required for Sencha Cmd packages to work.
//@require @packageOverrides

Ext.useShims = true;

// do disable caching enable line below
// Ext.Loader.setConfig('disableCaching', false);

Ext.application({
    name: 'TrackAnnot',
    extend: 'TrackAnnot.Application',
    autoCreateViewport: true
});

// Uncomment to load static demo track
//Ext.onReady(function() {
//    // overwrite web service urls to static files
//    var ctrl = TrackAnnot.getApplication().getController('Main');
//    ctrl.setupUrls('trackers.json', 'demo/tracker.json');
//});

Add comment how to use static demo track during development.


#!/usr/bin/env node

var nconf = require('nconf');
var toS3  = require('couch-to-s3');
var cred  = require('./lib/cred.js');

nconf.file('dev', 'config-dev.json').argv().env().file('config.json');
nconf.defaults({
	"dbUrl": "http://localhost:5984",
	"dbName": "database",
	"s3BucketName": "bucket",
	"awsCredentialsPath": ".aws/credentials"
});

var awsCred = cred(nconf.get('awsCredentialsPath'));

var options = {
	db: {
		url: nconf.get('dbUrl'),
		name: nconf.get('dbName')
	},
	aws: {
		accessKeyId: awsCred.aws_access_key_id,
		secretAccessKey: awsCred.aws_secret_access_key,
		bucket: nconf.get('s3BucketName')
	}
};

toS3(options, function (err, body) {
	if (err) {
		console.log("FAILURE");
		console.log(err.message);
		console.log(err.error);
		return;
	}

	console.log("SUCCESS");
	console.log(body.response.headers);
});

#!/usr/bin/env node

var nconf = require('nconf');
var toS3  = require('couch-to-s3');
var cred  = require('./lib/cred.js');

nconf.file('dev', 'config-dev.json').argv().env().file('config.json');
nconf.defaults({
	"dbUrl": "http://localhost:5984",
	"dbName": "database",
	"s3BucketName": "bucket",
	"awsCredentialsPath": ".aws/credentials"
});

var awsCred = cred(nconf.get('awsCredentialsPath'));

var db = {
	url: nconf.get('dbUrl'),
	name: nconf.get('dbName')
};

var aws = {
	accessKeyId: awsCred.aws_access_key_id,
	secretAccessKey: awsCred.aws_secret_access_key,
	bucket: nconf.get('s3BucketName')
};

toS3(db, aws, function (err, body) {
	if (err) {
		console.log("FAILURE");
		console.log(err.message);
		console.log(err.error);
		return;
	}

	console.log("SUCCESS");
	console.log(body.response.headers);
});

Use new copy API (db, aws, callback)


Fix spelling of Emmely de Wilt's name


'use strict';

/**
 * Events provides abstract event messaging.
 *
 * @constructor
 * @param {Object} provider [description]
 */
function Events(provider) {
  this.provider = provider;
}

/**
 * Returns a topic.
 *
 * @param  {string} name Topic name
 * @return {Topic}      Topic
 */
Events.prototype.topic = function* topic(name) {
  return yield this.provider.topic(name);
};

module.exports.Events = Events;

'use strict';

const _ = require('lodash');

/**
 * Events provides abstract event messaging.
 *
 * @constructor
 * @param {Object} provider [description]
 */
function Events(provider) {
  if (_.isNil(provider)) {
    throw new Error('provider does not exist');
  }
  this.provider = provider;
}

/**
 * Returns a topic.
 *
 * @param  {string} name Topic name
 * @return {Topic}      Topic
 */
Events.prototype.topic = function* topic(name) {
  return yield this.provider.topic(name);
};

module.exports.Events = Events;

Check arguments in Events constructor


/**
 * Register JSON utilities
 */

function registerJSON (cli, options, done) {
  cli.fs.registerSerializer('json', function (obj, opts) {
    if (opts.pretty) {
      return JSON.stringify(obj, null, 2)
    } else {
      return JSON.stringify(obj)
    }
  })

  cli.fs.registerDeserializer('json', function (data, opts) {
    return JSON.parse(data)
  })

  done()
}

/**
 * Exports
 */

module.exports = registerJSON

/**
 * Register JSON utilities
 */

function registerJSON (cli, options, done) {
  cli.fs.registerSerializer('json', function (obj, opts) {
    opts = opts || {}

    if (opts.pretty) {
      return JSON.stringify(obj, null, 2)
    } else {
      return JSON.stringify(obj)
    }
  })

  cli.fs.registerDeserializer('json', function (data, opts) {
    return JSON.parse(data)
  })

  done()
}

/**
 * Exports
 */

module.exports = registerJSON

Add options null guard to JSON serializer


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

const {
  useImperativeHandle,
  useRef,
} = React;

const TextField = React.forwardRef(({
  className,
  type = 'text',
  icon,
  ...props
}, ref) => {
  const refInput = useRef(null);
  useImperativeHandle(ref, () => ({
    get value() { return refInput.current.value; },
  }));

  return (

      <input
        ref={refInput}
        className="TextField-input"
        type={type}
        {...props}
      />
      {icon ? (

{icon}

      ) : null}

  );
});

TextField.propTypes = {
  className: PropTypes.string,
  type: PropTypes.string,
  icon: PropTypes.node,
};

export default TextField;

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

const {
  useEffect,
  useImperativeHandle,
  useRef,
} = React;

const TextField = React.forwardRef(({
  className,
  type = 'text',
  icon,
  autoFocus,
  ...props
}, ref) => {
  const refInput = useRef(null);
  useImperativeHandle(ref, () => ({
    get value() { return refInput.current.value; },
  }));
  useEffect(() => {
    if (autoFocus) {
      refInput.current?.focus();
    }
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  return (

      <input
        ref={refInput}
        className="TextField-input"
        type={type}
        {...props}
      />
      {icon ? (

{icon}

      ) : null}

  );
});

TextField.propTypes = {
  className: PropTypes.string,
  type: PropTypes.string,
  icon: PropTypes.node,
  autoFocus: PropTypes.bool,
};

export default TextField;

Make autoFocus work again in dialogs


$(function() {
  var $tabs = $('#search-results-tabs'),
      $searchForm = $('.js-search-hash');

  if($tabs.length > 0){
    $tabs.tabs({ 'defaultTab' : getDefaultSearchTabIndex(), scrollOnload: true });
  }

  function getDefaultSearchTabIndex(){
    var tabIds = $('.nav-tabs a').map(function(i, el){
          return $(el).attr('href').split('#').pop();
        }),
        $defaultTab = $('input[name=tab]'),
        selectedTab = $.inArray($defaultTab.val(), tabIds);

    return selectedTab > -1 ? selectedTab : 0;
  }

  if($searchForm.length){
    $tabs.on('tabChanged', updateSearchForm);
  }

  function updateSearchForm(e, hash){
    if(typeof hash !== 'undefined'){
      var $defaultTab = $('input[name=tab]');

      if($defaultTab.length === 0){
        $defaultTab = $('<input type="hidden" name="tab">');
        $searchForm.prepend($defaultTab);
      }

      $defaultTab.val(hash);
    }
  }
});

$(function() {
  var $tabs = $('#search-results-tabs'),
      $searchForm = $('.js-search-hash');

  if($tabs.length > 0){
    $tabs.tabs({ 'defaultTab' : getDefaultSearchTabIndex(), scrollOnload: true });
  }

  function getDefaultSearchTabIndex(){
    var tabIds = $('.search-navigation a').map(function(i, el){
          return $(el).attr('href').split('#').pop();
        }),
        $defaultTab = $('input[name=tab]'),
        selectedTab = $.inArray($defaultTab.val(), tabIds);

    return selectedTab > -1 ? selectedTab : 0;
  }

  if($searchForm.length){
    $tabs.on('tabChanged', updateSearchForm);
  }

  function updateSearchForm(e, hash){
    if(typeof hash !== 'undefined'){
      var $defaultTab = $('input[name=tab]');

      if($defaultTab.length === 0){
        $defaultTab = $('<input type="hidden" name="tab">');
        $searchForm.prepend($defaultTab);
      }

      $defaultTab.val(hash);
    }
  }
});

Fix selector for tab selection


FlowRouter.route('/', {
    name: 'home',
    action() {
        GAnalytics.pageview();
        BlazeLayout.render('MainLayout')
    }
});

FlowRouter.route('/cases', {
    name: 'cases',
    action() {
        GAnalytics.pageview();
        BlazeLayout.render('MainLayout', { main: 'Cases' });
    }
});

FlowRouter.route('/cases/create', {
    name: 'create-case',
    action() {
        GAnalytics.pageview();
        BlazeLayout.render('MainLayout', { main: 'NewCase' });
    }
});

FlowRouter.route('/cases/:id', {
    name: 'single-case',
    action() {
        GAnalytics.pageview();
        BlazeLayout.render('MainLayout', { main: 'Case' });
    }
});

if (Meteor.isClient) {
    Accounts.onLogin(function() {
        FlowRouter.go('cases');
    });

    Accounts.onLogout(function() {
        FlowRouter.go('home');
    });
}

FlowRouter.triggers.enter([function() {
    if (!Meteor.userId()) {
        FlowRouter.go('home');
    }
}]);

FlowRouter.route('/', {
    name: 'home',
    action() {
        if (Meteor.userId()) {
            FlowRouter.go('cases');
        }
        GAnalytics.pageview();
        BlazeLayout.render('HomeLayout')
    }
});

FlowRouter.route('/cases', {
    name: 'cases',
    action() {
        GAnalytics.pageview();
        BlazeLayout.render('MainLayout', { main: 'Cases' });
    }
});

FlowRouter.route('/cases/create', {
    name: 'create-case',
    action() {
        GAnalytics.pageview();
        BlazeLayout.render('MainLayout', { main: 'NewCase' });
    }
});

FlowRouter.route('/cases/:id', {
    name: 'single-case',
    action() {
        GAnalytics.pageview();
        BlazeLayout.render('MainLayout', { main: 'Case' });
    }
});

Add redirect for login/logout and implement route auth


export function clamp(val, min, max) {
  return Math.max(Math.min(val, max), min)
}

export function clamp(val, min, max) {
  if (val > max) {
    if (max === 10 && val === 10) // Willpower, essence can be exactly 10
      val = 10
    else if (max <= 10)
      val = val % 10
    else
      val = max
  }

  if (val < min)
    val = min

  return val
}

Improve input handling for attributes, abilities, essence, willpower


/* eslint-disable */
if (typeof global !== 'undefined') {
  // Node.js injections for Mocha tests
  global.chai = require('chai');
  global.path = require('path');
  global.noflo = require('../../src/lib/NoFlo');
  global.flowtrace = require('flowtrace');
  global.baseDir = process.cwd();
} else {
  // Browser injections for Mocha tests
  window.noflo = require('noflo');
  window.baseDir = 'browser';
  window.flowtrace = require('flowtrace');
}

/* eslint-disable */
if (typeof global !== 'undefined') {
  // Node.js injections for Mocha tests
  global.chai = require('chai');
  global.path = require('path');
  global.noflo = require('../../lib/NoFlo');
  global.flowtrace = require('flowtrace');
  global.baseDir = process.cwd();
} else {
  // Browser injections for Mocha tests
  window.noflo = require('noflo');
  window.baseDir = 'browser';
  window.flowtrace = require('flowtrace');
}

Use the transpiled version in tests


$(document).ready(function() {
  setTimeout(function() {
    var button = $("a.js-nav-week")[0];
    if (button) {
      button.click();
    }
  }, 500);
});

$(document).ready(function() {
  setTimeout(function() {
    var button = $("a.js-nav-week")[0];
    if (button) {
      button.click();
    }
  }, 500);

  setTimeout(function() {
    var link = $("a.js-copy-over-rows-from-most-recent-timesheet")[0];
    if (link) {
      link.click();
    }
  }, 500);
});

Copy over previous rows in harvest


var AWS = require('../core');

AWS.util.update(AWS.EC2.prototype, {
  /**
   * @api private
   */
  setupRequestListeners: function setupRequestListeners(request) {
    request.removeListener('extractError', AWS.EventListeners.Query.EXTRACT_ERROR);
    request.addListener('extractError', this.extractError);
  },

  /**
   * @api private
   */
  extractError: function extractError(resp) {
    // EC2 nests the error code and message deeper than other AWS Query services.
    var httpResponse = resp.httpResponse;
    var data = new AWS.XML.Parser({}).parse(httpResponse.body.toString() || '');
    if (data.Errors)
      resp.error = AWS.util.error(new Error(), {
        code: data.Errors.Error.Code,
        message: data.Errors.Error.Message
      });
    else
      resp.error = AWS.util.error(new Error(), {
        code: httpResponse.statusCode,
        message: null
      });
  }
});

var AWS = require('../core');

AWS.util.update(AWS.EC2.prototype, {
  /**
   * @api private
   */
  setupRequestListeners: function setupRequestListeners(request) {
    request.removeListener('extractError', AWS.EventListeners.Query.EXTRACT_ERROR);
    request.addListener('extractError', this.extractError);

    if (request.operation === 'copySnapshot') {
      request.onAsync('validate', this.buildCopySnapshotPresignedUrl);
    }
  },

  /**
   * @api private
   */
  buildCopySnapshotPresignedUrl: function buildCopySnapshotPresignedUrl(req, done) {
    if (req.params.PresignedUrl || req._subRequest) {
      return done();
    }

    req.params = AWS.util.copy(req.params);
    req.params.DestinationRegion = req.service.config.region;

    var config = AWS.util.copy(req.service.config);
    delete config.endpoint;
    config.region = req.params.SourceRegion;
    config.signatureVersion = 'v4';
    var svc = new req.service.constructor(config);
    var newReq = svc[req.operation](req.params);
    newReq._subRequest = true;
    newReq.presign(function(err, url) {
      if (err) done(err);
      else {
        req.params.PresignedUrl = url;
        done();
      }
    });
  },

  /**
   * @api private
   */
  extractError: function extractError(resp) {
    // EC2 nests the error code and message deeper than other AWS Query services.
    var httpResponse = resp.httpResponse;
    var data = new AWS.XML.Parser({}).parse(httpResponse.body.toString() || '');
    if (data.Errors)
      resp.error = AWS.util.error(new Error(), {
        code: data.Errors.Error.Code,
        message: data.Errors.Error.Message
      });
    else
      resp.error = AWS.util.error(new Error(), {
        code: httpResponse.statusCode,
        message: null
      });
  }
});

Implement copySnapshot customization to add PresignedUrl


import camelCase from './camel-case.js';

const isCamelCase = (a) => {
  const match = /(^[a-z]+)([A-Z][a-z]+)+/g.exec(a);
  return (match) ? match[0] === a : false;
};

export default isCamelCase;

Remove import from isCamelCase function


export default function ({ $axios }) {
  $axios.defaults.baseURL = process.env.API_URL || 'https://5jojq7ahg5.execute-api.us-east-1.amazonaws.com/prod';
  $axios.defaults.headers.post['Content-Type'] = 'application/json';
}

export default function ({ $axios }) {
  $axios.defaults.baseURL = process.env.API_URL || 'https://csk7a21fb4.execute-api.us-east-1.amazonaws.com/dev';
  $axios.defaults.headers.post['Content-Type'] = 'application/json';
}

Revert api url back to dev for now.


;(function(){//IFEE

angular.module('brewKeeper')
  .controller('createNewRecipe', function($scope, $http, $location){
    $scope.recipe = { }
    $scope.recipe.orientation = "Standard";

    $scope.submit=function(){
      var username = ""
      $http.get('https://brew-keeper-api.herokuapp.com/api/whoami/')
        .then(function(response){
          username = response.data.username;
          $http.post('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/', $scope.recipe)
            .success(function (data) {
              var id = data.id
              $location.path('/users/' + username + '/recipes/' + id);
            })
        $scope.recipe= { };

      })
  } //end submit function

  })//controller for creating new step

})();//END IFEE

;(function(){//IFEE

angular.module('brewKeeper')
  .controller('createNewRecipe', function($scope, $http, $location){
    $scope.recipe = { }
    $scope.recipe.orientation = "Standard";

    $scope.createNew=function(){
      var username = ""
      $http.get('https://brew-keeper-api.herokuapp.com/api/whoami/')
        .then(function(response){
        $("form-placeholder").removeClass("changed")

          username = response.data.username;
          $http.post('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/', $scope.recipe)
            .success(function (data) {
              var id = data.id
              $location.path('/users/' + username + '/recipes/' + id);
            })
        $scope.recipe= { };
      })
  } //end submit function

  $(".form-placeholder").on("change", function(){
    $(this).addClass("changed");
  })
  })//controller for creating new step

})();//END IFEE

Change prefilled gray suggestiosn to black by adding class.