var app = require("app");  // Module to control application life.
var BrowserWindow = require("browser-window");  // Module to create native browser window.

// Keep a global reference of the window object, if you don"t, the window will
// be closed automatically when the javascript object is GCed.
var mainWindow = null;

// Quit when all windows are closed.
app.on("window-all-closed", function() {
  app.quit();
});

// This method will be called when Electron has done everything
// initialization and ready for creating browser windows.
app.on("ready", function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    show: false,
    "auto-hide-menu-bar": true,
    icon: __dirname + "/static/images/icon.png"
  });

  // and load the index.html of the app.
  mainWindow.loadUrl("file://" + __dirname + "/static/index.html");

  // Emitted when the window is closed.
  mainWindow.on("closed", function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
});

var app = require("app");  // Module to control application life.
var BrowserWindow = require("browser-window");  // Module to create native browser window.

// Keep a global reference of the window object, if you don"t, the window will
// be closed automatically when the javascript object is GCed.
var mainWindow = null;

// Prevent multiple instances of Down Quark
if (app.makeSingleInstance(function (commandLine, workingDirectory) {
  if (mainWindow) {
    if (mainWindow.isMinimized()) mainWindow.restore();
    mainWindow.focus();
  }
  return true;
})) {
  app.quit();
  return;
}

// Quit when all windows are closed.
app.on("window-all-closed", function() {
  app.quit();
});

// This method will be called when Electron has done everything
// initialization and ready for creating browser windows.
app.on("ready", function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    show: false,
    "auto-hide-menu-bar": true,
    icon: __dirname + "/static/images/icon.png"
  });

  // and load the index.html of the app.
  mainWindow.loadUrl("file://" + __dirname + "/static/index.html");

  // Emitted when the window is closed.
  mainWindow.on("closed", function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
});

Make it a single instance app


var postcss = require('postcss');

module.exports = postcss.plugin('postcss-prefixer-keyframes', function (opts) {
    opts = opts || {};

    var prefix = opts.prefix || '';

    var usedKeyframes = [];

    return function (css, result) {

        /* Keyframes */
        css.walkAtRules(/keyframes$/, function (keyframes) {
            usedKeyframes.push(keyframes.params);
            keyframes.params = String(prefix+keyframes.params);
        });

        css.walkDecls(/animation/, function (decl) {
            var animationName = decl.value.split(' ');
            if (usedKeyframes.indexOf(animationName[0]) > -1) {
                animationName[0] = String(prefix + animationName[0]);
                decl.value = animationName.join(' ');
            }
        });
    };
});

var postcss = require('postcss');

module.exports = postcss.plugin('postcss-prefixer-keyframes', function (opts) {
    opts = opts || {};

    var prefix = opts.prefix || '';

    var usedKeyframes = [];

    return function (css, result) {

        /* Keyframes */
        css.walkAtRules(/keyframes$/, function (keyframes) {
            usedKeyframes.push(keyframes.params);
            keyframes.params = String(prefix+keyframes.params);
        });

        css.walkDecls(/animation/, function (decl) {
            var animations = decl.value.split(',');

            for (var i=0; i<animations.length; i++) {
                var animationName = animations[i].trim().split(' ');
                console.log(animationName[0]);
                if (usedKeyframes.indexOf(animationName[0]) > -1) {
                    animationName[0] = String(prefix + animationName[0]);
                }
                animations[i] = animationName.join(' ');
            }
            decl.value = animations.join(',');
        });
    };
});

Fix animation prefix to work with multiple keyframes in one animation


'use strict';

module.exports = {
  module: {
    loaders: [
      { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ }
    ]
  },
  output: {
    library: 'Redux',
    libraryTarget: 'umd'
  },
  resolve: {
    extensions: ['', '.js']
  }
};

'use strict';

module.exports = {
  module: {
    loaders: [
      { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ }
    ]
  },
  output: {
    library: 'Stateful',
    libraryTarget: 'umd'
  },
  resolve: {
    extensions: ['', '.js']
  }
};

Fix typo from copying over configs.


const fs = require('fs');
const path = require('path');

describe('node data resolution', () => {
  const relativeDataPath = './data/data.json';

  it('should be able to resolve data files through relative paths', () => {
    const resolvedRelativeDataPath = require.resolve(relativeDataPath);
    const dataContent = fs.readFileSync(resolvedRelativeDataPath);
    expect(JSON.parse(dataContent)).toEqual({ "value": 42 });
  });
  it('should be able to resolve data files through absolute paths', () => {
    const resolvedAbsoluteDataPath = require.resolve(path.join(__dirname, relativeDataPath));
    const dataContent = fs.readFileSync(resolvedAbsoluteDataPath);
    expect(JSON.parse(dataContent)).toEqual({ "value": 42 });
  });
  it('should throw when resolving files that are outside the sandbox', () => {
    if (process.platform.startsWith('win')) {
      // On Windows, file location is the original one and not sandboxed.
      // This means we cannot correctly exclude relative paths that aren't part of the runfiles.
      return;
    }
    // This file exists in the source folder but is not in the data array.
    expect(() => require.resolve('./data/missing-data.json')).toThrow();
  });
});

const fs = require('fs');
const path = require('path');

describe('node data resolution', () => {
  const relativeDataPath = './data/data.json';

  it('should be able to resolve data files through relative paths', () => {
    const resolvedRelativeDataPath = require.resolve(relativeDataPath);
    const dataContent = fs.readFileSync(resolvedRelativeDataPath);
    expect(JSON.parse(dataContent)).toEqual({ "value": 42 });
  });
  it('should be able to resolve data files through absolute paths', () => {
    const resolvedAbsoluteDataPath = require.resolve(path.join(__dirname, relativeDataPath));
    const dataContent = fs.readFileSync(resolvedAbsoluteDataPath);
    expect(JSON.parse(dataContent)).toEqual({ "value": 42 });
  });
  it('should throw when resolving files that are outside the sandbox', () => {
    if (process.platform.startsWith('win')) {
      // On Windows, file location is the original one and not sandboxed.
      // This means we cannot correctly exclude relative paths that aren't part of the runfiles.
      return;
    }
    // This file exists in the source folder but is not in the data array.
    expect(() => require.resolve('./data/missing-data.json')).toThrow();
  });
  it('should be able to resolve paths relative to data files', () => {
    if (process.platform.startsWith('win')) {
      // On Windows, file location is the original one and not sandboxed.
      // This means we cannot resolve paths relative to data files back to built files.
      return;
    }

    const resolvedRelativeDataPath = require.resolve(relativeDataPath);
    const thisFilePath = __filename;
    const relativePathFromDataToThisFile = path.join('../', path.basename(thisFilePath));
    const resolvedPathFromDataToThisFile = require.resolve(path.join(
      path.dirname(resolvedRelativeDataPath), relativePathFromDataToThisFile));

    expect(resolvedPathFromDataToThisFile).toEqual(thisFilePath);
  });
});

Add test for relative resolution from data files


var grunt = require('grunt');

var makeOptions = function (options) {

    var baseOptions = {
        base: null,
        prefix: 'grunt-',
        verbose: false
    };

    if (options) {
        for (var key in options) {
            if (options.hasOwnProperty(key)) {
                baseOptions[key] = options[key];
            }
        }
    }

    return baseOptions;
};

module.exports = function (gulp, options) {
    var tasks = getTasks(options);

    for (var name in tasks) {
        if (tasks.hasOwnProperty(name)) {
            var fn = tasks[name];
            gulp.task(name, fn);
        }
    }

};

var getTasks = module.exports.tasks = function (options) {
    var opt = makeOptions(options);

    if (opt.base) {
        grunt.file.setBase(opt.base);
    }

    grunt.task.init([]);

    var gruntTasks = grunt.task._tasks,
        finalTasks = {};

    for (var name in gruntTasks) {
        if (gruntTasks.hasOwnProperty(name)) {
            (function (name) {
                finalTasks[opt.prefix + name] = function (cb) {
                    if (opt.verbose) {
                        console.log('[grunt-gulp] Running Grunt "' + name + '" task...');
                    }
                    grunt.tasks([name], { force: true }, function () {
                        if (opt.verbose) {
                            grunt.log.ok('[grunt-gulp] Done running Grunt "' + name + '" task.');
                        }
                        cb();
                    });
                };
            })(name);
        }
    }

    return finalTasks;
};

var grunt = require('grunt');

var makeOptions = function (options) {

    var baseOptions = {
        base: null,
        prefix: 'grunt-',
        verbose: false
    };

    if (options) {
        for (var key in options) {
            if (options.hasOwnProperty(key)) {
                baseOptions[key] = options[key];
            }
        }
    }

    return baseOptions;
};

module.exports = function (gulp, options) {
    var tasks = getTasks(options);

    for (var name in tasks) {
        if (tasks.hasOwnProperty(name)) {
            var fn = tasks[name];
            gulp.task(name, fn);
        }
    }

};

var getTasks = module.exports.tasks = function (options) {
    var opt = makeOptions(options);

    if (opt.base) {
        grunt.file.setBase(opt.base);
    }

    grunt.task.init([]);

    var gruntTasks = grunt.task._tasks,
        finalTasks = {};

    for (var name in gruntTasks) {
        if (gruntTasks.hasOwnProperty(name)) {
            (function (name) {
                finalTasks[opt.prefix + name] = function (cb) {
                    if (opt.verbose) {
                        console.log('[grunt-gulp] Running Grunt "' + name + '" task...');
                    }
                    grunt.util.spawn({
                        cmd: 'grunt',
                        args: [name, '--force']
                    }, function() {
                        if (opt.verbose) {
                            grunt.log.ok('[grunt-gulp] Done running Grunt "' + name + '" task.');
                        }
                        cb();
                    });
            };
            })(name);
        }
    }

    return finalTasks;
};

Use grunt.util.spawn to execute the grunt task. This makes sure grunt configuration set via grunt.option is properly read again


'use strict';

var asyncDone = require('async-done');

function settle(fn, done){
  asyncDone(fn, function(error, result){
    var settled = {};

    if(error != null){
      settled.state = 'error';
      settled.value = error;
    } else {
      settled.state = 'success';
      settled.value = result;
    }

    done(undefined, settled);
  });
}

module.exports = settle;

'use strict';

var asyncDone = require('async-done');

function settle(fn, done){
  asyncDone(fn, function(error, result){
    var settled = {};

    if(error != null){
      settled.state = 'error';
      settled.value = error;
    } else {
      settled.state = 'success';
      settled.value = result;
    }

    done(null, settled);
  });
}

module.exports = settle;

Use null instead of undefined for errors


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

import { TYPE } from './../utils/constant';

function ProgressBar({
  delay,
  isRunning,
  closeToast,
  type,
  hide,
  className,
  rtl
}) {
  const style = {
    animationDuration: `${delay}ms`,
    animationPlayState: isRunning ? 'running' : 'paused',
    opacity: hide ? 0 : 1
  };
  style.WebkitAnimationPlayState = style.animationPlayState;

  const classNames = cx(
    'Toastify__progress-bar',
    `Toastify__progress-bar--${type}`,
    className,
    {
      'Toastify__progress-bar--rtl': rtl
    }
  );

  return (
    <div className={classNames} style={style} onAnimationEnd={closeToast} />
  );
}

ProgressBar.propTypes = {
  /**
   * The animation delay which determine when to close the toast
   */
  delay: PropTypes.number.isRequired,

  /**
   * Whether or not the animation is running or paused
   */
  isRunning: PropTypes.bool.isRequired,

  /**
   * Func to close the current toast
   */
  closeToast: PropTypes.func.isRequired,

  /**
   * Support rtl content
   */
  rtl: PropTypes.bool.isRequired,

  /**
   * Optional type : info, success ...
   */
  type: PropTypes.string,

  /**
   * Hide or not the progress bar
   */
  hide: PropTypes.bool,

  /**
   * Optionnal className
   */
  className: PropTypes.string
};

ProgressBar.defaultProps = {
  type: TYPE.DEFAULT,
  hide: false
};

export default ProgressBar;

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

import { TYPE } from './../utils/constant';

function ProgressBar({
  delay,
  isRunning,
  closeToast,
  type,
  hide,
  className,
  rtl
}) {
  const style = {
    animationDuration: `${delay}ms`,
    animationPlayState: isRunning ? 'running' : 'paused',
    opacity: hide ? 0 : 1
  };

  const classNames = cx(
    'Toastify__progress-bar',
    `Toastify__progress-bar--${type}`,
    className,
    {
      'Toastify__progress-bar--rtl': rtl
    }
  );

  return (
    <div className={classNames} style={style} onAnimationEnd={closeToast} />
  );
}

ProgressBar.propTypes = {
  /**
   * The animation delay which determine when to close the toast
   */
  delay: PropTypes.number.isRequired,

  /**
   * Whether or not the animation is running or paused
   */
  isRunning: PropTypes.bool.isRequired,

  /**
   * Func to close the current toast
   */
  closeToast: PropTypes.func.isRequired,

  /**
   * Support rtl content
   */
  rtl: PropTypes.bool.isRequired,

  /**
   * Optional type : info, success ...
   */
  type: PropTypes.string,

  /**
   * Hide or not the progress bar
   */
  hide: PropTypes.bool,

  /**
   * Optionnal className
   */
  className: PropTypes.string
};

ProgressBar.defaultProps = {
  type: TYPE.DEFAULT,
  hide: false
};

export default ProgressBar;

Remove webkit prefix, not needed anymore


'use strict';
module.exports = function (str, opts) {
	if (typeof str !== 'string') {
		throw new TypeError('Expected a string');
	}

	opts = opts || {};

	return str + ' & ' + (opts.postfix || 'rainbows');
};

'use strict';
var empty = require('is-empty');
var trim = require('trim');

module.exports = function (id, lang) {

	// Check if id is string and not empty.
	if (typeof id !== 'string') {
		throw new TypeError('Expected a string for id.');
	}
	if ( empty(id) ) {
		throw new TypeError('The application id mustn\'t be empty.');
	} else {
		// Remove leading/trailing white space.
		id = 'id=' + trim(id);
	}

	// Check if lang is string and not empty.
	if (typeof lang !== 'string') {
		lang = '';
	}
	if ( ! empty(lang) ) {
		lang = '&hl=' + trim(lang);
	}

	return 'https://play.google.com/store/apps/details?' + id + lang;
};

Return google play store links with or without language specification.


import { ItemTypes } from './constants';

const demoFolderStructure = {
  0: {
    type: ItemTypes.ROOT,
    content: [1, 7]
  },
  1: {
    type: ItemTypes.FOLDER,
    name: 'Answers',
    content: [2, 3, 6]
  },
  2: {
    type: ItemTypes.FOLDER,
    name: 'Read',
    content: []
  },
  3: {
    type: ItemTypes.FOLDER,
    name: 'Unread',
    content: [4, 8, 9, 5, 10, 11]
  },
  4: {
    type: ItemTypes.FOLDER,
    name: 'Empty Folder',
    content: []
  },
  5: {
    type: ItemTypes.FILE,
    name: 'From Baleog.msg'
  },
  6: {
    type: ItemTypes.FILE,
    name: 'From Olaf.msg'
  },
  7: {
    type: ItemTypes.FILE,
    name: 'From Erik.msg'
  },
  8: {
    type: ItemTypes.FILE,
    name: 'From Balrog.msg'
  },
  9: {
    type: ItemTypes.FOLDER,
    name: 'Another Folder',
    content: []
  },
  10: {
    type: ItemTypes.FILE,
    name: 'Gandalf'
  },
  11: {
    type: ItemTypes.FILE,
    name: 'Frodo'
  }
};

export default demoFolderStructure;

import { ItemTypes } from './constants';

const demoFolderStructure = {
  0: {
    type: ItemTypes.ROOT,
    content: [1, 7]
  },
  1: {
    type: ItemTypes.FOLDER,
    name: 'Folder 1',
    content: [2, 3, 6]
  },
  2: {
    type: ItemTypes.FOLDER,
    name: 'Folder 2',
    content: []
  },
  3: {
    type: ItemTypes.FOLDER,
    name: 'Folder 3',
    content: [4, 8, 9, 5, 10, 11]
  },
  4: {
    type: ItemTypes.FOLDER,
    name: 'Folder 4',
    content: []
  },
  5: {
    type: ItemTypes.FILE,
    name: 'File 5'
  },
  6: {
    type: ItemTypes.FILE,
    name: 'File 6'
  },
  7: {
    type: ItemTypes.FILE,
    name: 'File 7'
  },
  8: {
    type: ItemTypes.FILE,
    name: 'File 8'
  },
  9: {
    type: ItemTypes.FOLDER,
    name: 'Folder 9',
    content: []
  },
  10: {
    type: ItemTypes.FILE,
    name: 'File 10'
  },
  11: {
    type: ItemTypes.FILE,
    name: 'File 11'
  }
};

export default demoFolderStructure;

Rename items in demo folder structure


'use strict'

var svg = require('virtual-dom/virtual-hyperscript/svg')
var bezier = require('bezier-easing')
var Size = require('create-svg-size')
var Circle = require('./circle')
var Mask = require('./mask')

module.exports = Circles

function Circles (data) {
  return function render (time) {
    var mask = Mask(renderInner(data.radius, time))
    var outer = renderOuter(data.radius, time, data.fill, mask.id)

    var options = Size({x: data.radius * 2, y: data.radius * 2})

    return svg('svg', options, [
      mask.vtree,
      outer
    ])
  }
}

function renderInner (radius, time) {
  var coefficient = 0.95 * curve(time < 0.5 ? time : 1 - time)

  return Circle({
    radius: radius * coefficient,
    center: radius
  })
}

function renderOuter (radius, time, fill, mask) {
  var coefficent = curve(time < 0.5 ? 1 - time : time)

  return Circle({
    id: 'circle-bounce-circle',
    radius: radius * coefficent,
    center: radius,
    fill: fill,
    mask: mask
  })
}

function curve (value) {
  return bezier(0.10, 0.45, 0.9, 0.45).get(value)
}

'use strict'

var svg = require('virtual-dom/virtual-hyperscript/svg')
var bezier = require('bezier-easing')
var Size = require('create-svg-size')
var Circle = require('./circle')
var Mask = require('./mask')

module.exports = Circles

function Circles (data) {
  return function render (time) {
    var mask = Mask(renderInner(data.radius, time))
    var outer = renderOuter(data.radius, time, data.fill, mask.id)

    var options = Size({x: data.radius * 2, y: data.radius * 2})

    return svg('svg', options, [
      mask.vtree,
      outer
    ])
  }
}

function renderInner (radius, time) {
  var coefficient = 0.95 * curve(time < 0.5 ? time : 1 - time)

  return Circle({
    radius: radius * coefficient,
    center: radius
  })
}

function renderOuter (radius, time, fill, mask) {
  var coefficent = curve(time < 0.5 ? 1 - time : time)

  return Circle({
    id: 'circle-bounce-circle',
    radius: radius * coefficent,
    center: radius,
    fill: fill,
    mask: mask
  })
}

function curve (value) {
  return bezier(0, 0.5, 1, 0.5).get(value)
}

Use a symmetric eazing curve with flat center


wrapAsync = (Meteor.wrapAsync)? Meteor.wrapAsync : Meteor._wrapAsync;
Mongo.Collection.prototype.aggregate = function(pipelines) {
  var coll = MongoInternals.defaultRemoteCollectionDriver().mongo.db.collection(this._name);
  return wrapAsync(coll.aggregate.bind(coll))(pipelines);
}

wrapAsync = (Meteor.wrapAsync)? Meteor.wrapAsync : Meteor._wrapAsync;
Mongo.Collection.prototype.aggregate = function(pipelines) {
  var coll;
  if (undefined !== this.rawCollection) {
    // >= Meteor 1.0.4
    coll = this.rawCollection();
  } else {
	// < Meteor 1.0.4
    coll = this._getCollection();
  }
  return wrapAsync(coll.aggregate.bind(coll))(pipelines);
}

Use exposed method not a hack, and make backwards compatable


/* jshint node: true */

'use strict';

function getTaskConfig(projectConfig) {
	var taskConfig = {
		svgSrc: projectConfig.paths.src.svgs + '**/*.svg',
		outDir: '',

		settings: {
		    "dest": "",
		    "mode": {
		        "css": {
		        	"dest": projectConfig.paths.src.sass + "/_objects",
		        	"sprite": process.cwd() + '/' + projectConfig.paths.dest.svgs + '/svgsprite.svg',
		            "render": {
		            	"scss": {
		            		'dest': "_objects.svgsprite.scss"
		            	}
		            },
		           	"layout": "packed",
		           	"prefix": ".svgsprite--%s",
		           	"dimensions": "__dimensions",
		           	"bust": true
		        }
	    	}
	    }

	}

	return taskConfig;
}

module.exports = getTaskConfig;

/* jshint node: true */

'use strict';

var path = require('path');

function getTaskConfig(projectConfig) {
	var taskConfig = {
		svgSrc: projectConfig.paths.src.svgs + '**/*.svg',
		outDir: '',
		settings: {
		    dest: '', // If using modes, leave this empty and configure on a mode level - determines where to put the SVG symbol/css sprite/def
		    mode: {
				css: {
					dest: projectConfig.paths.src.sass + '/_tools', // where it specifically puts the css file (not where it'd put the def / symbol etc)
					sprite: path.join(process.cwd(), projectConfig.paths.dest.images, '/sprite.svg'), // where it puts the svg file and what it names it
					layout: 'packed', // default - layout of svg in the sprite
					prefix: '.svgsprite--%s', // custom - prefix for svg classes
					dimensions: '__dimensions', // custom - suffix for dimensions class e.g. .svg--hamburger__dimensions
					bust: false, // default - cache busting,
					render: {
						scss: { // make it render scss and not css
							dest: '_tools.svg-sprite.scss', // scss file name
							template: path.join(process.cwd(), projectConfig.paths.src.images, '/svg-sprite-sass.tpl')
						}
					}
				}
	    	},
			shape: {
				spacing: { // Spacing related options
					padding: 4, // Padding around all shapes
					box: 'content' // Padding strategy (similar to CSS `box-sizing`)
				}
			}
	    }

	}

	return taskConfig;
}

module.exports = getTaskConfig;

Update default svg config to include scss render, template file


import Ember from 'ember';
import $ from 'jquery';

export default Ember.Component.extend({
  attributeBindings: ['role'],
  didInsertElement () {
    const $emEl = this.$();
    const $modalBtn = $('[em-modal-open]');

    $emEl.attr('role', 'dialog');
    $modalBtn.click(() => {
      $emEl.fadeIn(100);
    });

    $emEl.click(event => {
      if (!$(event.target).parents(`#${this.$().attr('id')}`).length) {
        $emEl.fadeOut(100);
      }
    });
  }
});

import Ember from 'ember';
import $ from 'jquery';

export default Ember.Component.extend({
  attributeBindings: ['role'],
  didInsertElement () {
    const $Element = this.$();
    const $modalBtn = $('[em-modal-open]');

    $Element.attr('role', 'dialog');
    $modalBtn.click(() => {
      $Element.fadeIn(100);
      this.lockBackground($Element);
    });

    $Element.click(event => {
      if (!$(event.target).parents(`#${this.$().attr('id')}`).length) {
        $Element.fadeOut(100);
      }
    });
  },

  lockBackground (el) {
    const focusableElementQuery = 'select:not([disabled]), button:not([disabled]), [tabindex="0"], input:not([disabled]), a[href]';
    const backgroundActiveEl = document.activeElement;
    const focusableElements = el.find(focusableElementQuery);
    const firstEl = focusableElements[0];
    const lastEl = focusableElements[focusableElements.length - 1];

    // Focus first element in modal
    firstEl.focus();
    el.keydown(event => {
      // If Esc pressed
      if (event.keyCode === 27) {
        el.fadeOut(100);
        return backgroundActiveEl.focus();
      }

      // Trap Tab key while modal open
      this.trapTabKey(event, firstEl, lastEl);
    });
  },

  trapTabKey (event, ...params) {
    const [ firstEl, lastEl ] = params;

    if (event.keyCode === 9) {
      if (event.shiftKey) {
        if (document.activeElement === firstEl) {
          event.preventDefault();
          return lastEl.focus();
        }
      } else {
        if (document.activeElement === lastEl) {
          event.preventDefault();
          return firstEl.focus();
        }
      }
    }
  }
});

Add trap tab key functionality to modal


protractor.expect = {
  challengeSolved: function (context) {
    describe('(shared)', () => {
      beforeEach(() => {
        browser.get('/#/score-board')
      })

      it("challenge '" + context.challenge + "' should be solved on score board", () => {
        expect(element(by.id(context.challenge + '.solved')).getAttribute('hidden')).toBeFalsy()
        expect(element(by.id(context.challenge + '.notSolved')).getAttribute('hidden')).toBeTruthy()
      })
    })
  }
}

protractor.beforeEach = {
  login: function (context) {
    let oldToken
    describe('(shared)', () => {
      beforeEach(() => {
        oldToken = localStorage.getItem('token')
        browser.get('/#/login')
        element(by.id('email')).sendKeys(context.email)
        element(by.id('password')).sendKeys(context.password)
        element(by.id('loginButton')).click()
      })

      it('should have logged in user "' + context.email + '" with password "' + context.password + '"', () => {
        expect(localStorage.getItem('token')).not.toMatch(oldToken)
      })
    })
  }
}

protractor.expect = {
  challengeSolved: function (context) {
    describe('(shared)', () => {
      beforeEach(() => {
        browser.get('/#/score-board')
      })

      it("challenge '" + context.challenge + "' should be solved on score board", () => {
        expect(element(by.id(context.challenge + '.solved')).getAttribute('hidden')).toBeFalsy()
        expect(element(by.id(context.challenge + '.notSolved')).getAttribute('hidden')).toBeTruthy()
      })
    })
  }
}

protractor.beforeEach = {
  login: function (context) {
    describe('(shared)', () => {
      beforeEach(() => {
        browser.get('/#/login')
        element(by.id('email')).sendKeys(context.email)
        element(by.id('password')).sendKeys(context.password)
        element(by.id('loginButton')).click()
      })

      it('should have logged in user "' + context.email + '" with password "' + context.password + '"', () => {
        expect(browser.getCurrentUrl()).toMatch(/\/search/) // TODO Instead check for uib-tooltip of <i> with fa-user-circle
      })
    })
  }
}

Switch back to login check via location compare


'use strict';

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

/**
 * Defines all linting task.
 *
 * @function
 * @memberof! GruntLint
 * @private
 * @param {Object} grunt - The grunt instance
 * @returns {Object} Grunt config object
 */
module.exports = function (grunt) {
    grunt.registerTask('lint', 'Linting tasks.', [
        'concurrent:lint'
    ]);

    var lintTasks = [
        'markdownlint:full',
        'htmlhint:full',
        'htmllint:full',
        'csslint:full',
        'jsonlint:full',
        'jshint:full',
        'jshint:test',
        'jscs:full',
        'jscs:test',
        'filenames:full'
    ];

    /*istanbul ignore next*/
    if (global.build.options.buildConfig.es6Support) {
        lintTasks = lintTasks.concat([
            'stylelint:full',
            'yaml_validator:full'
        ]);
    }

    /*istanbul ignore next*/
    if (global.build.options.buildConfig.jslintEnabled) {
        lintTasks = lintTasks.concat([
            'jslint:full',
            'jslint:test'
        ]);
    }

    /*istanbul ignore else*/
    if (helper.isESlintSupported(global.build.options.buildConfig)) {
        lintTasks = lintTasks.concat([
            'eslint:full',
            'eslint:test'
        ]);
    }

    return {
        tasks: {
            concurrent: {
                lint: {
                    target: lintTasks
                }
            }
        }
    };
};

'use strict';

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

/**
 * Defines all linting task.
 *
 * @function
 * @memberof! GruntLint
 * @private
 * @param {Object} grunt - The grunt instance
 * @returns {Object} Grunt config object
 */
module.exports = function (grunt) {
    grunt.registerTask('lint', 'Linting tasks.', [
        'concurrent:lint'
    ]);

    var lintTasks = [
        'markdownlint:full',
        'htmlhint:full',
        'htmllint:full',
        'csslint:full',
        'jsonlint:full',
        'jshint:full',
        'jshint:test',
        'jscs:full',
        'jscs:test',
        'filenames:full'
    ];

    /*istanbul ignore next*/
    if (global.build.options.buildConfig.es6Support) {
        lintTasks.push('yaml_validator:full');

        if (global.build.options.buildConfig.nodeMajorVersion >= 6) {
            lintTasks.push('stylelint:full');
        }
    }

    /*istanbul ignore next*/
    if (global.build.options.buildConfig.jslintEnabled) {
        lintTasks = lintTasks.concat([
            'jslint:full',
            'jslint:test'
        ]);
    }

    /*istanbul ignore else*/
    if (helper.isESlintSupported(global.build.options.buildConfig)) {
        lintTasks = lintTasks.concat([
            'eslint:full',
            'eslint:test'
        ]);
    }

    return {
        tasks: {
            concurrent: {
                lint: {
                    target: lintTasks
                }
            }
        }
    };
};

Add support of node >=6 grunt tasks


var DemoApp = function(desktop) {
  this.demoWindow = desktop.createWindow({
    x: 50,
    y: 50,
    title: "Demo App!"
  });
  this.demoWindow.body.innerHTML = "0, 0";
  this.on("tick", function() {
    var posElement = this.demoWindow.body.querySelector(".posDisplay");
    posElement.textContent = this.demoWindow.x + ", " + this.demoWindow.y;
  });
};
DemoApp.shortname = "demoapp";
DemoApp.fullname = "Demo Application";
DemoApp.description = "Provides a demonstration of basic application functions";

module.exports = DemoApp;

var DemoApp = function(desktop) {
  this.demoWindow = desktop.createWindow(this, {
    x: 50,
    y: 50,
    title: "Demo App!"
  });
  this.demoWindow.body.innerHTML = "0, 0";
  this.on("tick", function() {
    var posElement = this.demoWindow.body.querySelector(".posDisplay");
    posElement.textContent = this.demoWindow.x + ", " + this.demoWindow.y;
  });
};
DemoApp.shortname = "demoapp";
DemoApp.fullname = "Demo Application";
DemoApp.description = "Provides a demonstration of basic application functions";

module.exports = DemoApp;

Fix createWindow call to reference app


'use strict';

var
  growler = require('growler')
;

module.exports = function(applicationOptions) {

  applicationOptions = applicationOptions || {};

  var growl = new growler.GrowlApplication('Gulp', applicationOptions);

  growl.setNotifications({
    Gulp: {}
  });

  function notify(notificationOptions, callback) {

    growl.register(function(success, err) {
      if (!success) {
        throw callback(err);
      }

      notificationOptions.text = notificationOptions.message;
      delete notificationOptions.message;

      growl.sendNotification('Gulp', notificationOptions, function(success, err) {
        return callback(err, success);
      });

    });

  }

  return notify;

};

'use strict';

var
  fs      = require('fs'),
  util    = require('util'),
  growler = require('growler')
;

module.exports = function(applicationOptions) {

  applicationOptions = util._extend({
    icon: fs.readFileSync(__dirname + '/gulp.png')
  }, applicationOptions || {});

  var growl = new growler.GrowlApplication('Gulp', applicationOptions);

  growl.setNotifications({
    Gulp: {}
  });

  function notify(notificationOptions, callback) {

    growl.register(function(success, err) {
      if (!success) {
        throw callback(err);
      }

      // Rename 'message' property to 'text'
      notificationOptions.text = notificationOptions.message;
      delete notificationOptions.message;

      growl.sendNotification('Gulp', notificationOptions, function(success, err) {
        return callback(err, success);
      });

    });

  }

  return notify;

};

Set gulp icon as default


'use strict';

const BaseStep = require('./base-step');

module.exports = class VoidStep extends BaseStep {
  start() {}
};

'use strict';

const BaseStep = require('./base-step');

module.exports = class VoidStep extends BaseStep {
  start() { return new Promise(() => {}); }
};

Return a promise from void step


var fs = require('fs');

function isSdkDirectory() {
  var cwd = process.cwd();

  var data = fs.readdirSync(cwd);

  data = data.map(item => item.toLowerCase());

  if (data.indexOf('scripts') > -1 &&
      data.indexOf('plugintester') &&
      data.indexOf('plugins') > -1 &&
      data.indexOf('package.json') > -1) {
    return true;
  }

  return false;
}

module.exports = isSdkDirectory;

var fs = require('fs');

function isSdkDirectory() {
  var cwd = process.cwd();

  var data = fs.readdirSync(cwd);

  data = data.map(item => item.toLowerCase());

  if (data.indexOf('scripts') > -1 &&
      data.indexOf('plugintester') > -1 &&
      data.indexOf('plugins') > -1 &&
      data.indexOf('package.json') > -1) {
    return true;
  }

  return false;
}

module.exports = isSdkDirectory;

Resolve SDK directory check issue


export default [
  {
    text: 'Use Cases',
    submenu: [
      { text: 'Service Discovery', url: '/discovery' },
      { text: 'Service Mesh', url: '/mesh' },
    ],
  },
  {
    text: 'Intro',
    url: '/intro',
    type: 'inbound',
  },
  {
    text: 'Learn',
    url: 'https://learn.hashicorp.com/consul',
    type: 'outbound',
  },
  {
    text: 'Docs',
    url: '/docs',
    type: 'inbound',
  },
  {
    text: 'API',
    url: '/api-docs',
    type: 'inbound',
  },
  {
    text: 'Community',
    url: '/community',
    type: 'inbound',
  },
  {
    text: 'Enterprise',
    url:
      'https://www.hashicorp.com/products/consul/?utm_source=oss&utm_medium=header-nav&utm_campaign=consul',
    type: 'outbound',
  },
]

export default [
  { text: 'Overview', url: '/', type: 'inbound' },
  {
    text: 'Use Cases',
    submenu: [
      { text: 'Service Discovery', url: '/discovery' },
      { text: 'Service Mesh', url: '/mesh' },
    ],
  },
  {
    text: 'Enterprise',
    url:
      'https://www.hashicorp.com/products/consul/?utm_source=oss&utm_medium=header-nav&utm_campaign=consul',
    type: 'outbound',
  },
  'divider',
  {
    text: 'Learn',
    url: 'https://learn.hashicorp.com/consul',
    type: 'outbound',
  },
  {
    text: 'Docs',
    url: '/docs',
    type: 'inbound',
  },
  {
    text: 'API',
    url: '/api-docs',
    type: 'inbound',
  },
  {
    text: 'Community',
    url: '/community',
    type: 'inbound',
  },
]

Adjust nav to new recommended layout.


import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import MVT from '../src/ol/format/MVT.js';
import VectorTileLayer from '../src/ol/layer/VectorTile.js';
import VectorTileSource from '../src/ol/source/VectorTile.js';

const map = new Map({
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 2
  }),
  layers: [new VectorTileLayer({
    source: new VectorTileSource({
      format: new MVT(),
      url: 'https://basemaps.arcgis.com/v1/arcgis/rest/services/World_Basemap/VectorTileServer/tile/{z}/{y}/{x}.pbf'
    })
  })]
});

map.on('pointermove', showInfo);

const info = document.getElementById('info');
function showInfo(event) {
  const features = map.getFeaturesAtPixel(event.pixel);
  if (!features) {
    info.innerText = '';
    info.style.opacity = 0;
    return;
  }
  const properties = features[0].getProperties();
  info.innerText = JSON.stringify(properties, null, 2);
  info.style.opacity = 1;
}

import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import MVT from '../src/ol/format/MVT.js';
import VectorTileLayer from '../src/ol/layer/VectorTile.js';
import VectorTileSource from '../src/ol/source/VectorTile.js';

const map = new Map({
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 2
  }),
  layers: [new VectorTileLayer({
    source: new VectorTileSource({
      format: new MVT(),
      url: 'https://basemaps.arcgis.com/v1/arcgis/rest/services/World_Basemap/VectorTileServer/tile/{z}/{y}/{x}.pbf'
    })
  })]
});

map.on('pointermove', showInfo);

const info = document.getElementById('info');
function showInfo(event) {
  const features = map.getFeaturesAtPixel(event.pixel);
  if (features.length == 0) {
    info.innerText = '';
    info.style.opacity = 0;
    return;
  }
  const properties = features[0].getProperties();
  info.innerText = JSON.stringify(properties, null, 2);
  info.style.opacity = 1;
}

Update test for no features


'use strict';

var app = angular.module('settingsWindow', ['schemaForm']);

app.run(function($rootScope) {
    $rootScope.GUI = require('nw.gui');
    $rootScope.AppName = $rootScope.GUI.App.manifest.name;
    $rootScope.Version = $rootScope.GUI.App.manifest.version;
    $rootScope.Window = $rootScope.GUI.Window.get();
    $rootScope.Pussh = global.Pussh;

    $rootScope.Window.focus();
});

app.controller('settings', function($scope, $rootScope) {
    var Pussh = $rootScope.Pussh;

    $scope.services = Pussh.services.list();
    $scope.settings = Pussh.settings.get();
    $scope.selectedService = Pussh.services.get($scope.settings.selectedService);
    $scope.serviceSettings = $scope.selectedService.getSettings();

    $scope.$watch('selectedService', function(service) {
        $scope.settings.selectedService = service._name;
        $scope.serviceSettings = $scope.selectedService.settings;

        Pussh.settings.save();
    });

    $scope.$watch('settings', function() {
        Pussh.settings.save();
    }, true);

    $scope.$watch('serviceSettings', function() {
        $scope.selectedService.saveSettings();
    }, true);
});

'use strict';

var app = angular.module('settingsWindow', []);

app.run(function($rootScope) {
    $rootScope.GUI = require('nw.gui');
    $rootScope.AppName = $rootScope.GUI.App.manifest.name;
    $rootScope.Version = $rootScope.GUI.App.manifest.version;
    $rootScope.Window = $rootScope.GUI.Window.get();
    $rootScope.Pussh = global.Pussh;

    $rootScope.Window.focus();
});

app.controller('settings', function($scope, $rootScope) {
    var Pussh = $rootScope.Pussh;

    $scope.services = Pussh.services.list();
    $scope.settings = Pussh.settings.get();
    $scope.selectedService = Pussh.services.get($scope.settings.selectedService);
    $scope.serviceSettings = $scope.selectedService.getSettings();

    $scope.$watch('selectedService', function(service) {
        $scope.settings.selectedService = service._name;
        $scope.serviceSettings = $scope.selectedService.settings;

        Pussh.settings.save();
    });

    $scope.$watch('settings', function() {
        Pussh.settings.save();
    }, true);

    $scope.$watch('serviceSettings', function() {
        $scope.selectedService.saveSettings();
    }, true);
});

Remove loaded module which was removed


$.fn.userAutocomplete = function () {
  'use strict';

  function formatUser(user) {
    return Select2.util.escapeMarkup(user.email);
  }

  this.select2({
    minimumInputLength: 1,
    multiple: true,
    initSelection: function (element, callback) {
      Spree.ajax({
        url: Spree.routes.users_api,
        data: {
          ids: element.val()
        },
        success: function(data) {
          callback(data.users);
        }
      });
    },
    ajax: {
      url: Spree.routes.users_api,
      datatype: 'json',
      params: { "headers": { "X-Spree-Token": Spree.api_key } },
      data: function (term) {
        return {
          m: 'or',
          email_start: term,
          addresses_firstname_start: term,
          addresses_lastname_start: term
        };
      },
      results: function (data) {
        return {
          results: data.users,
          more: data.current_page < data.pages
        };
      }
    },
    formatResult: formatUser,
    formatSelection: formatUser
  });
};

$(document).ready(function () {
  $('.user_picker').userAutocomplete();
});

$.fn.userAutocomplete = function () {
  'use strict';

  function formatUser(user) {
    return Select2.util.escapeMarkup(user.email);
  }

  this.select2({
    minimumInputLength: 1,
    multiple: true,
    initSelection: function (element, callback) {
      Spree.ajax({
        url: Spree.routes.users_api,
        data: {
          ids: element.val()
        },
        success: function(data) {
          callback(data.users);
        }
      });
    },
    ajax: {
      url: Spree.routes.users_api,
      datatype: 'json',
      params: { "headers": { "X-Spree-Token": Spree.api_key } },
      data: function (term) {
        return {
          q: {
            m: 'or',
            email_start: term,
            addresses_firstname_start: term,
            addresses_lastname_start: term
          }
        };
      },
      results: function (data) {
        return {
          results: data.users,
          more: data.current_page < data.pages
        };
      }
    },
    formatResult: formatUser,
    formatSelection: formatUser
  });
};

$(document).ready(function () {
  $('.user_picker').userAutocomplete();
});

Fix user picker search request


import React from 'react'
import { Router, Route, hashHistory } from 'react-router'

import App from './App'
import Home from './Home'
import Overview from './Overview'
import MatchReportContainer from './containers/MatchReportContainer'
import NewGameContainer from './containers/NewGameContainer'
import ScoreGameContainer from './containers/ScoreGameContainer'
import NewTeamContainer from './containers/NewTeamContainer'
import EditTeamContainer from './containers/EditTeamContainer'
import DashboardContainer from './containers/DashboardContainer'

export default (
  <Router history={hashHistory}>
    <Route path='/game/:id/score' component={ScoreGameContainer} />
    <Route path='/' component={Home} />
    <Route path='/' component={App}>
      <Route path='/game/new' component={NewGameContainer} />
      <Route path='/game' component={Overview} />
      <Route path='/game/:id' component={Home} />
      <Route path='/game/:id/report' component={MatchReportContainer} />
      <Route path='/dashboard' component={DashboardContainer} />
      <Route path='/team/new' component={NewTeamContainer} />
      <Route path='/team/:id/edit' component={EditTeamContainer} />
    </Route>
  </Router>
)

import React from 'react'
import { Router, Route, hashHistory } from 'react-router'

import App from './App'
import Home from './Home'
import Overview from './Overview'
import MatchReportContainer from './containers/MatchReportContainer'
import NewGameContainer from './containers/NewGameContainer'
import EditGameContainer from './containers/EditGameContainer'
import ScoreGameContainer from './containers/ScoreGameContainer'
import NewTeamContainer from './containers/NewTeamContainer'
import EditTeamContainer from './containers/EditTeamContainer'
import DashboardContainer from './containers/DashboardContainer'

export default (
  <Router history={hashHistory}>
    <Route path='/game/:id/score' component={ScoreGameContainer} />
    <Route path='/' component={Home} />
    <Route path='/' component={App}>
      <Route path='/game' component={Overview} />
      <Route path='/game/new' component={NewGameContainer} />
      <Route path='/game/:id' component={Home} />
      <Route path='/game/:id/report' component={MatchReportContainer} />
      <Route path='/game/:id/edit' component={EditGameContainer} />
      <Route path='/dashboard' component={DashboardContainer} />
      <Route path='/team/new' component={NewTeamContainer} />
      <Route path='/team/:id/edit' component={EditTeamContainer} />
    </Route>
  </Router>
)

Make route to /game/:id/edit link to EditGame


import { StyleSheet } from 'react-native';

const theme = {
  colours: {
    primary: '#fbb632',
    secondary: '#2c2c2c',
    dark: '#212121',
    light: '#bebebe',
  }
}

const navigatorStyle = {
  navBarTextColor: 'white',
  navBarBackgroundColor: theme.colours.dark
}

const appStyles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#141414',
  },

  card: {
    flex: 1,
    margin: 10,
    backgroundColor: theme.colours.secondary,
    borderColor: theme.colours.secondary,
    borderWidth: 0,
    borderRadius: 4,
    shadowOffset: { width: 0, height: 2 },
    shadowRadius: 1.5,
    shadowOpacity: 0.5,
    shadowColor: '#666666'
  }
});

export default appStyles;
export { theme, navigatorStyle };

import { StyleSheet } from 'react-native';

const theme = {
  colours: {
    primary: '#fbb632',
    secondary: '#2c2c2c',
    dark: '#212121',
    light: '#bebebe',
  }
}

const navigatorStyle = {
  navBarTextColor: 'white',
  navBarBackgroundColor: theme.colours.dark
}

const appStyles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#141414',
  },

  card: {
    flex: 1,
    margin: 10,
    backgroundColor: theme.colours.secondary,
    borderColor: '#414141',
    borderWidth: 0,
    borderBottomWidth: 2,
    borderRadius: 3
  }
});

export default appStyles;
export { theme, navigatorStyle };

Use borderBottom instead of shadow for cards


import { Comments } from "meteor/example-forum";

Comments.addField([
  // This commment will appear in alignment forum view
  {
    fieldName: 'af',
    fieldSchema: {
      type: Boolean,
      optional: true,
      label: "Alignment Forum",
      defaultValue: false,
      viewableBy: ['guests'],
      editableBy: ['alignmentForum'],
      insertableBy: ['alignmentForum'],
      control: 'AlignmentCheckbox'
    }
  },

  {
    fieldName: 'afBaseScore',
    fieldSchema: {
      type: Number,
      optional: true,
      label: "Alignment Base Score",
      viewableBy: ['guests'],
    }
  },
])

import { Comments } from "meteor/example-forum";

Comments.addField([
  // This commment will appear in alignment forum view
  {
    fieldName: 'af',
    fieldSchema: {
      type: Boolean,
      optional: true,
      label: "Alignment Forum",
      defaultValue: false,
      viewableBy: ['guests'],
      editableBy: ['alignmentVoters'],
      insertableBy: ['alignmentVoters'],
      control: 'AlignmentCheckbox'
    }
  },

  {
    fieldName: 'afBaseScore',
    fieldSchema: {
      type: Number,
      optional: true,
      label: "Alignment Base Score",
      viewableBy: ['guests'],
    }
  },
])

Comment UI appears on AF for users if they own the post and have at least 1 AF Karma, but not if they don't.


var m = angular.module("routeUrls", []);

m.factory("urls", function($route) {

    var pathsByName = {};
    angular.forEach($route.routes, function (route, path) {
        if (route.name) {
            pathsByName[route.name] = path;
        }
    });

    var path = function (name, params) {
        var url = pathsByName[name] || "/";
        angular.forEach(params || {}, function (value, key) {
            url = url.replace(new RegExp(":" + key + "(?=/|$)"), value);
        });
        return url;
    };

    return {
        path: path,

        href: function (name, params) {
            return "#" + path(name, params);
        }
    };
});

var m = angular.module("routeUrls", []);

m.factory("urls", function($route) {

    var pathsByName = {};
    angular.forEach($route.routes, function (route, path) {
        if (route.name) {
            pathsByName[route.name] = path;
        }
    });

    var regexs = {};

    var path = function (name, params) {
        var url = pathsByName[name] || "/";
        angular.forEach(params || {}, function (value, key) {
            var regex = regexs[key];

            if (regex === undefined) {
              regex = regexs[key] = new RegExp(":" + key + "(/|$)");
            }
            url = url.replace(regex, value);
        });
        return url;
    };

    return {
        path: path,

        href: function (name, params) {
            return "#" + path(name, params);
        }
    };
});

Improve ui responsiveness if urls.path is databound


$(document).ready(function() {

  $("#new-feature").hide();
  $(".edit-feature").hide();

  $("#new-feature-button").on("click", function(event) {
    event.preventDefault();
    $(this).hide();
    $("#interaction-block").html($("#new-feature").html());
  });

  $(".feature-item").on("click", function(event) {
    event.preventDefault();
    $("#interaction-block").html($("#"+$(this).text()).html());
    $("#new-feature-button").show();
  });

  $("#feature-filter").on("keyup", function(event) {
    // ...
  });

  $("#value-filter").on("keyup", function(event) {
    // ...
  });

});

$(document).ready(function() {

  $("#new-feature").hide();
  $(".edit-feature").hide();

  $("#new-feature-button").on("click", function(event) {
    event.preventDefault();
    $(this).hide();
    $(".alert").hide();
    $("#interaction-block").html($("#new-feature").html());
  });

  $(".feature-item").on("click", function(event) {
    event.preventDefault();
    $(".alert").hide();
    $("#interaction-block").html($("#"+$(this).text()).html());
    $("#new-feature-button").show();
  });

  $("#feature-filter").on("keyup", function(event) {
    // ...
  });

  $("#value-filter").on("keyup", function(event) {
    // ...
  });

});

Hide any alerts when user starts to interact with "Features" page again after completing an action.


// Set the RequireJS configuration
require.config({
    paths : {
        "use" : "libs/use",
        "text" : "libs/text",
        "jquery" : "libs/jquery",
        "underscore" : "libs/underscore",
        "backbone" : "libs/backbone",
        "handlebars" : "libs/handlebars-1.0.0.beta.6"
    },
    use : {
        "underscore" : {
            attach : "_"
        },

        "backbone" : {
            deps : ["use!underscore", "jquery"],
            attach : function(_, $) {
                return Backbone;
            }
        },

        "handlebars" : {
            attach: "Handlebars"
        }
    }
});

// Initialization
require([
    "dashboard/DashboardView",
    "dashboard/DashboardRouter",
    "datum/DatumCollection"
], function(DashboardView, DashboardRouter, DatumCollection) {
    // Initialize the DashboardView
    window.dashboard = new DashboardView();

    // Initialize the DashboardRouter and start listening for URL changes
    window.router = new DashboardRouter();
    Backbone.history.start();

    // Initialize our list of Datum
    window.datumList = new DatumCollection();
});

// Set the RequireJS configuration
require.config({
    paths : {
        "use" : "libs/use",
        "text" : "libs/text",
        "jquery" : "libs/jquery",
        "underscore" : "libs/underscore",
        "backbone" : "libs/backbone",
        "handlebars" : "libs/handlebars-1.0.0.beta.6"
    },
    use : {
        "underscore" : {
            attach : "_"
        },

        "backbone" : {
            deps : ["use!underscore", "jquery"],
            attach : function(_, $) {
                return Backbone;
            }
        },

        "handlebars" : {
            attach: "Handlebars"
        }
    }
});

// Initialization
require([
    "dashboard/DashboardView",
    "dashboard/DashboardRouter",
    "datum/DatumCollection"
], function(DashboardView, DashboardRouter, DatumCollection) {
    // Initialize the DashboardView
    window.dashboard = new DashboardView();

    // Initialize the DashboardRouter and start listening for URL changes
    window.router = new DashboardRouter();

    // Initialize our list of Datum
    window.datumList = new DatumCollection();
});

Remove line that tries to sync the DatumCollection.


import reduce from 'lodash-es/reduce'
import merge from 'lodash-es/merge'

function bundleModules (modules) {
  let bundle = reduce(modules, (acc, v1, k1) => {
    reduce(v1, (acc2, v2, k2) => {
      if (k2 !== 'data') {
        v2 = reduce(v2, (acc3, v3, k3) => {
          acc3[k1 + '/' + k3] = v3
          return acc3
        }, {})
      }
      acc[k2] = merge(acc[k2], v2)
      return acc
    }, {})
    return acc
  }, {})

  return bundle
}

export default bundleModules

import reduce from 'lodash-es/reduce'
import merge from 'lodash-es/merge'

function bundleModules (modules) {
  let bundle = reduce(modules, (acc, v1) => {
    reduce(v1, (acc2, v2, k2) => {
      if (k2 !== 'data') {
        v2 = reduce(v2, (acc3, v3, k3) => {
          acc3[k3] = v3
          return acc3
        }, {})
      }
      acc[k2] = merge(acc[k2], v2)
      return acc
    }, {})
    return acc
  }, {})

  return bundle
}

export default bundleModules

Remove extra param for module indexing.


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

// this is the path of eslint-loader `index.js`
const ESLINT_PATH = `eslint-loader${path.sep}index.js`;

function getEslintOptions(rules) {
  const matcher = (rule) => rule.loader
    && typeof rule.loader === 'string'
    && rule.loader.endsWith(ESLINT_PATH);
  return getLoader(rules, matcher).options;
}

function rewireEslint(config, env) {
  // if `react-scripts` version < 1.0.0
  // **eslint options** is in `config`
  const oldOptions = config.eslint;
  // else `react-scripts` >= 1.0.0
  // **eslint options** is in `config.module.rules`
  const newOptions = getEslintOptions(config.module.rules);

  // Thx @Guria, with no break change.
  const options = oldOptions || newOptions;
  options.useEslintrc = true;
  options.ignore = true;
  return config;
}

module.exports = rewireEslint;

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

// this is the path of eslint-loader `index.js`
const ESLINT_PATH = `eslint-loader${path.sep}index.js`;

function getEslintOptions(rules) {
  const matcher = (rule) => rule.loader
    && typeof rule.loader === 'string'
    && rule.loader.endsWith(ESLINT_PATH);
  return getLoader(rules, matcher).options;
}

function rewireEslint(config, env, override = f => f) {
  // if `react-scripts` version < 1.0.0
  // **eslint options** is in `config`
  const oldOptions = config.eslint;
  // else `react-scripts` >= 1.0.0
  // **eslint options** is in `config.module.rules`
  const newOptions = getEslintOptions(config.module.rules);

  // Thx @Guria, with no break change.
  const options = oldOptions || newOptions;
  options.useEslintrc = true;
  options.ignore = true;

  override(options);

  return config;
}

module.exports = rewireEslint;

Allow eslint config options overrides


import {createAction} from 'redux-actions';
import values from 'lodash/values';
import {UnitActions} from './constants';
import {Action} from '../common/constants';
import {ApiResponse, UnitServices} from './constants';

export const fetchUnits = (/*params: Object*/): Action =>
  createAction(UnitActions.FETCH)({params: {
    service: `${values(UnitServices).join(',')}`,
    only: 'id,name,location',
    include: 'observations,services',
    page_size: 1000
  }});

export const receiveUnits = (data: ApiResponse): Action =>
  createAction(UnitActions.RECEIVE)(data);

export const clearSearch = () =>
  createAction(UnitActions.SEARCH_CLEAR)();

export const searchUnits = (input: string, params: Object): Action => {
  const init = {
    input,
    service: `${values(UnitServices).join(',')}`
  };

  params = Object.assign({}, init, params);
  return createAction(UnitActions.SEARCH_REQUEST)({params});
};

export const fetchSearchSuggestions = (input: string): Action =>
  createAction(UnitActions.FETCH_SEARCH_SUGGESTIONS)({params: {
    input,
    service: `${values(UnitServices).join(',')}`,
    page_size: 5
  }});

export const receiveSearchResults = (results: Array<Object>) =>
  createAction(UnitActions.SEARCH_RECEIVE)(results);

export const receiveSearchSuggestions = (results: Array<Object>) =>
  createAction(UnitActions.RECEIVE_SEARCH_SUGGESTIONS)(results);

import {createAction} from 'redux-actions';
import values from 'lodash/values';
import {UnitActions} from './constants';
import {Action} from '../common/constants';
import {ApiResponse, UnitServices} from './constants';

export const fetchUnits = (/*params: Object*/): Action =>
  createAction(UnitActions.FETCH)({params: {
    service: `${values(UnitServices).join(',')}`,
    only: 'id,name,location,street_address,address_zip',
    include: 'observations,services',
    page_size: 1000
  }});

export const receiveUnits = (data: ApiResponse): Action =>
  createAction(UnitActions.RECEIVE)(data);

export const clearSearch = () =>
  createAction(UnitActions.SEARCH_CLEAR)();

export const searchUnits = (input: string, params: Object): Action => {
  const init = {
    input,
    service: `${values(UnitServices).join(',')}`
  };

  params = Object.assign({}, init, params);
  return createAction(UnitActions.SEARCH_REQUEST)({params});
};

export const fetchSearchSuggestions = (input: string): Action =>
  createAction(UnitActions.FETCH_SEARCH_SUGGESTIONS)({params: {
    input,
    service: `${values(UnitServices).join(',')}`,
    page_size: 5
  }});

export const receiveSearchResults = (results: Array<Object>) =>
  createAction(UnitActions.SEARCH_RECEIVE)(results);

export const receiveSearchSuggestions = (results: Array<Object>) =>
  createAction(UnitActions.RECEIVE_SEARCH_SUGGESTIONS)(results);

Include address information in unit fetch


/**
 * # Convert Release.json to Changes.md
 *
 * @param  {Array}  data The releases from Github's API
 * @param  {String} out  Changelog filename
 * @return {Stream} The file write stream
 */

var fs = require('fs');

module.exports = function (data, out) {
  var output = fs.createWriteStream(out);

  output.on('open', function () {
    data.forEach(function (release) {
      output.write('# '+ release.name + '\n\n');
      output.write( (new Date(release.published_at)).toDateString() + '\n\n');
      output.write(release.body.replace('\r\n', '\n') + '\n\n');
    });
    output.end();
  });

  return output;
};

/**
 * # Convert Release.json to Changes.md
 *
 * @param  {Array}  data The releases from Github's API
 * @param  {String} out  Changelog filename
 * @return {Stream} The file write stream
 */

var fs = require('fs');

module.exports = function (data, out) {
  var output = fs.createWriteStream(out);

  output.on('open', function () {
    output.write('# Changelog' + '\n\n');
    data.forEach(function (release) {
      output.write('## '+ release.name + '\n\n');
      output.write( (new Date(release.published_at)).toDateString() + '\n\n');
      output.write(release.body.replace('\r\n', '\n') + '\n\n');
    });
    output.end();
  });

  return output;
};

Make 'Changelog' h1, Versions h2


var d3 = require('d3');
var React = require('react');
var ReactDOM = require('react-dom');

var AnitmationCircle = React.createClass({

  displayName: 'AnitmationCircle',

  propTypes: {
    className: React.PropTypes.string,
    transitionTime: React.PropTypes.number.isRequired,
    position: React.PropTypes.array.isRequired,
    radius: React.PropTypes.number,
    cx: React.PropTypes.number,
    cy: React.PropTypes.number
  },

  getDefaultProps: function () {
    return {
      radius: 4,
      cx: 0,
      cy: 0
    };
  },

  componentDidMount: function () {
    d3.select(ReactDOM.findDOMNode(this))
      .attr('transform', 'translate(' + this.props.position + ')');
  },

  componentWillReceiveProps: function (props) {
    d3.select(ReactDOM.findDOMNode(this))
      .transition()
      .duration(props.transitionTime)
      .ease('linear')
      .attr('transform', 'translate(' + props.position + ')');
  },

  render: function () {
    var props = this.props;
    var radius = props.radius;
    var className = props.className;

    return (
      <circle className={className} r={radius} cx={props.cx} cy={props.cy} />
    );
  }
});

module.exports = AnitmationCircle;

var d3 = require('d3');
var React = require('react');
var ReactDOM = require('react-dom');

var AnitmationCircle = React.createClass({

  displayName: 'AnitmationCircle',

  propTypes: {
    className: React.PropTypes.string,
    transitionTime: React.PropTypes.number.isRequired,
    position: React.PropTypes.array.isRequired,
    radius: React.PropTypes.number,
    cx: React.PropTypes.number,
    cy: React.PropTypes.number
  },

  getInitialState: function () {
    return {
      didRenderBefore: false
    };
  },
  getDefaultProps: function () {
    return {
      radius: 4,
      cx: 0,
      cy: 0
    };
  },

  componentDidMount: function () {
    d3.select(ReactDOM.findDOMNode(this))
      .attr('transform', 'translate(' + this.props.position + ')');
  },

  componentWillReceiveProps: function (nextProps) {
    let node = ReactDOM.findDOMNode(this);

    // Handle first position to not animate into position
    // We need this because we get 0-data for graphs on the first render
    if (!this.state.didRenderBefore) {
      d3.select(node)
        .attr('transform', 'translate(' + nextProps.position + ')');

      this.setState({didRenderBefore: true});

      return;
    }

    d3.select(node)
      .transition()
      .duration(nextProps.transitionTime)
      .ease('linear')
      .attr('transform', 'translate(' + nextProps.position + ')');
  },

  render: function () {
    var props = this.props;
    var radius = props.radius;
    var className = props.className;

    return (
      <circle className={className} r={radius} cx={props.cx} cy={props.cy} />
    );
  }
});

module.exports = AnitmationCircle;

Make sure not to animate into position on first render


require('dotenv').load();
var express = require('express');
var notelyServerApp = express();
var Note = require('./models/note');
var bodyParser = require('body-parser');

// Allow ourselves to use `req.body` to work with form data
notelyServerApp.use(bodyParser.json());

// Cross-Origin Resource Sharing (CORS) middleware
notelyServerApp.use(function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
  next();
});

notelyServerApp.get('/notes', function(req, res) {
  Note.find().then(function(notes) {
    res.json(notes);
  });
});

notelyServerApp.post('/notes', function(req, res) {
  var note = new Note({
    title: req.body.note.title,
    body_html: req.body.note.body_html
  });

  note.save().then(function(noteData) {
    res.json({
      message: 'Saved!',
      note: noteData
    });
  });
});

notelyServerApp.listen(3030, function() {
  console.log('Listening on http://localhost:3030');
});

require('dotenv').load();
var express = require('express');
var notelyServerApp = express();
var Note = require('./models/note');
var bodyParser = require('body-parser');

// Allow ourselves to use `req.body` to work with form data
notelyServerApp.use(bodyParser.json());

// Cross-Origin Resource Sharing (CORS) middleware
notelyServerApp.use(function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
  next();
});

notelyServerApp.get('/notes', function(req, res) {
  Note.find().sort({ updated_at: 'desc' }).then(function(notes) {
    res.json(notes);
  });
});

notelyServerApp.post('/notes', function(req, res) {
  var note = new Note({
    title: req.body.note.title,
    body_html: req.body.note.body_html
  });

  note.save().then(function(noteData) {
    res.json({
      message: 'Saved!',
      note: noteData
    });
  });
});

notelyServerApp.listen(3030, function() {
  console.log('Listening on http://localhost:3030');
});

Order notes on the server (API) side.


var feature_flags = (function () {

var exports = {};

exports.mark_read_at_bottom = page_params.staging ||
                              _.contains(['mit.edu'], page_params.domain);
exports.summarize_read_while_narrowed = page_params.staging;
exports.twenty_four_hour_time = _.contains([],
                                  page_params.email);
exports.dropbox_integration = page_params.staging || _.contains(['dropbox.com'], page_params.domain);
exports.email_forwarding = page_params.staging;
exports.edit_appears_on_hover = true;

exports.mandatory_topics = _.contains([
    'customer7.invalid'
    ],
    page_params.domain
);

exports.collapsible = page_params.staging;

return exports;

}());

var feature_flags = (function () {

var exports = {};

exports.mark_read_at_bottom = page_params.staging ||
                              _.contains(['mit.edu'], page_params.domain);
exports.summarize_read_while_narrowed = page_params.staging;
exports.twenty_four_hour_time = _.contains([],
                                  page_params.email);
exports.dropbox_integration = page_params.staging || _.contains(['dropbox.com'], page_params.domain);
exports.email_forwarding = page_params.staging ||
                           _.contains(['customer6.invalid'], page_params.domain);
exports.edit_appears_on_hover = true;

exports.mandatory_topics = _.contains([
    'customer7.invalid'
    ],
    page_params.domain
);

exports.collapsible = page_params.staging;

return exports;

}());

Enable the email forwarding UI for CUSTOMER6.


import React from 'react';

function BrowserWarning() {
  return (

        Currently, Varaamo does not support IE11.
        We are investigating this issue and finding a solution.
        Meanwhile, use another browser (such as
         Chrome
        ,
         Firefox
        or
         Edge
        ).

        Varaamo ei tue IE11 selainta tällä hetkellä.
        Selvitämme ongelmaa sen ratkaisemiseksi.
        Sillä välin, käytä toista selainta (kuten
         Chrome
        ,
         Firefox
        tai
         Edge
        ).

  );
}

export default BrowserWarning;

import React from 'react';

function BrowserWarning() {
  return (

        Currently, Varaamo does not support IE11.
        We are investigating this issue and finding a solution.
        Meanwhile, use another browser (such as
         Chrome
        ,
         Firefox
        or
         Edge
        ).

        Varaamo ei tue IE11 selainta tällä hetkellä.
        Selvitämme ongelmaa sen ratkaisemiseksi.
        Sillä välin, käytä toista selainta (kuten
         Chrome
        ,
         Firefox
        tai
         Edge
        ).

        Varaamo fungerar inte längre med Internet Explorer 11.
        Vi arbetar med att lösa problemet.
        Under tiden så var vänlig och använd någon annan browser (t.ex
         Chrome
        ,
         Firefox
        eller
         Edge
        ).

  );
}

export default BrowserWarning;

Add swedish translation in browser warning message


export default class GameLoop {
  loop = () => {
    this.subscribers.forEach((callback) => {
      callback.call();
    });

    this.loopID = window.requestAnimationFrame(this.loop);
  }
  constructor() {
    this.subscribers = [];
    this.loopID = null;
  }
  start() {
    if (!this.loopID) {
      this.loop();
    }
  }
  stop() {
    window.cancelAnimationFrame(this.loop);
  }
  subscribe(callback) {
    return this.subscribers.push(callback);
  }
  unsubscribe(id) {
    this.subscribers.splice((id - 1), 1);
  }
}

export default class GameLoop {
  loop = () => {
    this.subscribers.forEach((callback) => {
      callback.call();
    });

    this.loopID = window.requestAnimationFrame(this.loop);
  }
  constructor() {
    this.subscribers = [];
    this.loopID = null;
  }
  start() {
    if (!this.loopID) {
      this.loop();
    }
  }
  stop() {
    window.cancelAnimationFrame(this.loop);
  }
  subscribe(callback) {
    return this.subscribers.push(callback);
  }
  unsubscribe(id) {
    this.subscribers.splice(this.subscribers.indexOf(id), 1);
  }
}

Fix native splicing as well


#!/usr/bin/env node

var Locket = require('../')
var cadence = require('cadence')
var path = require('path')
var crypto = require('crypto')
var seedrandom = require('seedrandom')

function pseudo (max) {
    var random = seedrandom()()
    while (random > max) {
        random = seedrandom()()
    }
    return random
}

cadence(function (step) {
    var locket = new Locket(path.join(path.join(__dirname, '../tmp'), 'put'))
    step(function () {
        locket.open({ createIfMissing: true }, step())
    }, function () {
        var entries = []
        var max = 10000
        var type, sha, entry, val = seedrandom(0)()
        for (var i = 0; i < 1024; i++) {
            type = (pseudo(2) % 2 == 0)
            sha = crypto.createHash('sha1')
            entry = new Buffer(4)
            entry.writeFloatLE(val, 0)
            sha.update(entry)

            entries.push({
                type: type,
                key: sha.digest('binary'),
                value: val
            })

            val = pseudo(max)
        }
        locket.batch(entries, step())
    }, function () {
        sha = crypto.createHash('sha1')
        first_key = new Buffer(4)
        first_key.writeFloatLE(seedrandom(0)(), 0)
        sha.update(first_key)
        locket.get(sha.digest('binary'), function (_, value) {
            console.log(value)
        })
        // ^^^ no idea what's going on here.
    })
})(function (error) {
    if (error) throw error
})

#!/usr/bin/env node

var Locket = require('../')
var cadence = require('cadence')
var path = require('path')
var crypto = require('crypto')
var seedrandom = require('seedrandom')

var random = (function () {
    var random = seedrandom(0)
    return function (max) {
        return Math.floor(random() * max)
    }
})()

cadence(function (step) {
    var locket = new Locket(path.join(path.join(__dirname, '../tmp'), 'put'))
    step(function () {
        locket.open({ createIfMissing: true }, step())
    }, function () {
        var entries = []
        var type, sha, buffer, value
        for (var i = 0; i < 1024; i++) {
            var value = random(10000)
            sha = crypto.createHash('sha1')
            buffer = new Buffer(4)
            buffer.writeUInt32BE(value, 0)
            sha.update(buffer)
            entries.push({
                key: sha.digest('binary'),
                value: value,
                type: !! random(1)
            })
        }
        console.log('here', entries.length)
        locket.batch(entries, step())
    })(7)
})(function (error) {
    if (error) throw error
})

Implement a write test for Locket.


"use strict";

var http = require('http');
var path = require('path');
var fs = require('fs');
var mime = require('mime');

function send404 (response) {
	response.writeHead(404, {"Content-type" : "text/plain"});
	response.write("Error 404: Resource not found");
	response.end();
}

function sendPage (response, filePath, fileContents) {
	response.writeHead(200, { "Content-type": mime.lookup(path.basename(filePath)) });
	response.end(fileContents);
}

function serverWorking (response, absPath) {
	fs.exists(absPath, function (exists) {
		if (exists) {
			fs.readFile(absPath, function (e, data) {
				if (e) send404(response);
				else sendPage(response, absPath, data);
			});
		} else send404(response);
	});
}

var server = http.createServer(function (request, response) {
	var filePath;
	if (request.url === '/') filePath = 'index.html';
	else if (request.url === '/bus-tracker' || request.url === '/bus-tracker/') filePath = 'bus-tracker.html';
	else filePath = request.url;

	serverWorking(response, "./" + filePath);
});

server.listen(process.env.PORT || 8080);
console.log("Please go to http://127.0.0.1:8080");

"use strict";

var http = require('http');
var path = require('path');
var fs = require('fs');
var mime = require('mime');

function send404 (response) {
	response.writeHead(404, {"Content-type" : "text/plain"});
	response.write("Error 404: Resource not found");
	response.end();
}

function sendPage (response, filePath, fileContents) {
	response.writeHead(200, { "Content-type": mime.lookup(path.basename(filePath)) });
	response.end(fileContents);
}

function serverWorking (response, absPath) {
	fs.exists(absPath, function (exists) {
		if (exists) {
			fs.readFile(absPath, function (e, data) {
				if (e) send404(response);
				else sendPage(response, absPath, data);
			});
		} else send404(response);
	});
}

var server = http.createServer(function (request, response) {
	var filePath;
	if (request.url === '/') filePath = 'bus-tracker.html';
	else filePath = request.url;

	serverWorking(response, "./" + filePath);
});

server.listen(process.env.PORT || 8080);
console.log("Please go to http://127.0.0.1:8080");

Put bus-tracker as default page


import React from 'react';
import CodeMirror from 'codemirror';
import './qwery-mode';
import Hint from './hint';
import Completion from './completion';
import QueryRunner from './query-runner';

export default class QueryEditor extends React.Component {

  constructor () {
    super();
    this.state = {};
  }

  componentDidMount () {
    var cm = CodeMirror(this._container, {
      lineNumbers: true,
      lineSeperator: '\n',
      value: '?Person "position held":P39 "President of Amerika":Q11696 ',
      mode: 'qwery',
      theme: 'qwery'
    });

    this.setState({cm: cm});
  }

  render () {
    var {cm} = this.state
    var hint, queryRunner;

    if (cm) {
      hint = (
	<Hint cm={cm}>
	<Completion cm={cm} />
	</Hint>
      );

      queryRunner = (
	<QueryRunner cm={cm}/>
      );
    }

    return (

 this._container = el}>

      {hint}

      {queryRunner}

    )
  }
}

import React from 'react';
import CodeMirror from 'codemirror';
import './qwery-mode';
import Hint from './hint';
import Completion from './completion';
import QueryRunner from './query-runner';

export default class QueryEditor extends React.Component {

  constructor () {
    super();
    this.state = {};
  }

  componentDidMount () {
    var cm = CodeMirror(this._container, {
      lineNumbers: true,
      lineSeperator: '\n',
      value: '?Person "position held":P39 "President of Amerika":Q11696 ',
      mode: 'qwery',
      theme: 'qwery'
    });

    var inloop = false;

    cm.on('change', function (cm, change) {
      var range;

      if (!inloop && change.text.length === 1 && change.text[0] === '"') {
	inloop = true;

	cm.replaceRange('"', cm.getCursor(), cm.getCursor());
	cm.setCursor({
	  line: cm.getCursor().line,
	  ch: cm.getCursor().ch - 1
	})

      } else {
	inloop = false;
      }
    });

    this.setState({cm: cm});
  }

  render () {
    var {cm} = this.state
    var hint, queryRunner;

    if (cm) {
      hint = (
	<Hint cm={cm}>
	<Completion cm={cm} />
	</Hint>
      );

      queryRunner = (
	<QueryRunner cm={cm}/>
      );
    }

    return (

 this._container = el}>

      {hint}

      {queryRunner}

    )
  }
}

Add simple closing quotation marks


require.config({

    // alias libraries paths
    paths: {
        'domReady': '../bower_components/requirejs-domready/domReady',
        'angular': '../bower_components/angular/angular',
        'jquery': '../bower_components/jquery/jquery',
        'ui.bootstrap': '../bower_components/angular-bootstrap/ui-bootstrap-tpls',
        'store': '../bower_components/store/store'
    },

    // angular does not support AMD out of the box, put it in a shim
    shim: {
        'angular': {
            exports: 'angular'
        },
        'ui.bootstrap': {
            deps: ['angular']
        }
    },

    // kick start application
    deps: ['./bootstrap']
});

require.config({

    // alias libraries paths
    paths: {
        'domReady': '../bower_components/requirejs-domready/domReady',
        'angular': '../bower_components/angular/angular',
        'jquery': '../bower_components/jquery/jquery',
        'masonry': '../bower_components/masonry/masonry',
        'lodash': '../bower_components/lodash/dist/lodash',
        'doc-ready/doc-ready': '../bower_components/doc-ready/doc-ready',
        'get-style-property/get-style-property': '../bower_components/get-style-property/get-style-property',
        'matches-selector/matches-selector': '../bower_components/matches-selector/matches-selector',
        'outlayer/item': '../bower_components/outlayer/item',
        'outlayer/outlayer': '../bower_components/outlayer/outlayer',
        'get-size/get-size': '../bower_components/get-size/get-size',
        'eventie/eventie': '../bower_components/eventie/eventie',
        'eventEmitter/EventEmitter': '../bower_components/eventEmitter/EventEmitter',
        'imagesLoaded': '../bower_components/imagesloaded/imagesloaded',
        'ui.bootstrap': '../bower_components/angular-bootstrap/ui-bootstrap-tpls',
        'store': '../bower_components/store/store'
    },

    // angular does not support AMD out of the box, put it in a shim
    shim: {
        'angular': {
            exports: 'angular'
        },
        'ui.bootstrap': {
            deps: ['angular']
        }
    },

    // kick start application
    deps: ['./bootstrap']
});

Add new dependencies paths to requirejs config


import React, { Component, PropTypes } from 'react';
import {
    Text,
    TouchableHighlight
} from 'react-native';

class Button extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    const {theme, disabled} = this.props;
    const {styles} = theme;

    const btnStyles = [styles.button];
    if (disabled) {
      btnStyles.push(styles.buttonDisabled);
    }

    return (
      <TouchableHighlight
          onPress={this.props.onPress}
          style={btnStyles}
          underlayColor="#a30000"
          activeOpacity={1}>
        <Text style={styles.buttonText}>{this.props.children}</Text>
      </TouchableHighlight>
    );
  }
}

Button.propTypes = {
  styles: PropTypes.object,
  onPress: PropTypes.func
};

export default Button;

import React, { PropTypes } from 'react';
import {
    Text,
    TouchableHighlight
} from 'react-native';

const Button = ({theme, disabled}) => {
  const {styles} = theme;
  const btnStyles = [styles.button];
  if (disabled) {
    btnStyles.push(styles.buttonDisabled);
  }

  return (
    <TouchableHighlight
        onPress={this.props.onPress}
        style={btnStyles}
        underlayColor="#a30000"
        activeOpacity={1}>
      <Text style={styles.buttonText}>{this.props.children}</Text>
    </TouchableHighlight>
  );
};

Button.propTypes = {
  styles: PropTypes.object,
  onPress: PropTypes.func
};

export default Button;

Make button a stateless component


angular.module('app')
.directive('selectOnClick', function () {
    return {
        restrict: 'A',
        link: function (scope, element) {
            var focusedElement;
            element.on('click', function () {
                if (focusedElement != this) {
                    this.select();
                    focusedElement = this;
                }
            });
            element.on('blur', function () {
                focusedElement = null;
            });
        }
    };
})

angular.module('app')
.directive('selectOnClick', function () {
    return {
        restrict: 'A',
        link: function (scope, element) {
            var focusedElement;
            element.on('click', function () {
                if (focusedElement != this) {
                    this.setSelectionRange(0, this.value.length);
                    focusedElement = this;
                }
            });
            element.on('blur', function () {
                focusedElement = null;
            });
        }
    };
})

Fix issue with selecting text input on iOS


module.exports = {
  'extends': './index.js',

  'plugins': [
    'react'
  ],

  'ecmaFeatures': {
    'jsx': true
  },

  'rules': {
    'react/display-name': 0,
    'react/jsx-boolean-value': 2,
    'react/jsx-no-undef': 2,
    'react/jsx-quotes': [2, 'single', 'avoid-escape'],
    'react/jsx-uses-react': 1,
    'react/jsx-uses-vars': 1,
    'react/no-did-mount-set-state': 1,
    'react/no-did-update-set-state': 1,
    'react/no-multi-comp': 1,
    'react/no-unknown-property': 2,
    'react/prop-types': 2,
    'react/react-in-jsx-scope': 1,
    'react/self-closing-comp': 2,
    'react/sort-comp': 2,
    'react/wrap-multilines': 2,
  }
};

module.exports = {
  'extends': './index.js',

  'plugins': [
    'react'
  ],

  'ecmaFeatures': {
    'jsx': true
  },

  'rules': {
    'react/display-name': 0,
    'react/jsx-boolean-value': 2,
    'react/jsx-no-undef': 2,
    'react/jsx-quotes': [2, 'single', 'avoid-escape'],
    'react/jsx-uses-react': 1,
    'react/jsx-uses-vars': 1,
    'react/no-did-mount-set-state': [1, 'allow-in-func'],
    'react/no-did-update-set-state': 1,
    'react/no-multi-comp': 1,
    'react/no-unknown-property': 2,
    'react/prop-types': 2,
    'react/react-in-jsx-scope': 1,
    'react/self-closing-comp': 2,
    'react/sort-comp': 2,
    'react/wrap-multilines': 2,
  }
};

Allow calling `setState` nested in a function in `componentDidMount`


/**
 * Has Vowels
 *
 * hasVowel tests if the String calling the function has a vowels
 *
 * @param {void}
 * @return {Boolean} returns true or false indicating if the string
 * has a vowel or not
 */
String.prototype.hasVowels = function() {
    var inputString = this;
    return /[aeiou]/gi.test(inputString);
};

String.prototype.toUpper = function() {
    return "";
};

/**
 * Has Vowels
 *
 * hasVowel tests if the String calling the function has a vowels
 *
 * @param {void}
 * @return {Boolean} returns true or false indicating if the string
 * has a vowel or not
 */
String.prototype.hasVowels = function() {
    var inputString = this;
    return /[aeiou]/gi.test(inputString);
};

/**
 * To Upper
 *
 * toUpper converts its calling string to all uppercase characters
 *
 * @param {void}
 * @return {String} returns an upperCase version of the calling string
 */
String.prototype.toUpper = function() {
    var inputString = this;
    var lowerCasePattern = /[a-z]/g;

    return inputString.replace(lowerCasePattern, function(item, position, string) {
        return String.fromCharCode(string.charCodeAt(position)-32);
    });
};

Modify toUpper to return uppercase of its calling string


import Vue from "vue";
import VueResource from "vue-resource";
import Carousel from "./components/Carousel.vue";
import styles from './carousel.css';

Vue.use(VueResource);
Vue.config.debug = true;

const carousel = new Vue({
    el: "#your-application-id",
    components: { Carousel },

    mounted() {
        // Debug purpose
        if(Vue.config.debug)
        {
            console.log("Debug is on");
        }
    }
});

Remove new line from the if


import cx from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';
import YouTubePlayerEmbed from './PlayerEmbed';

const YouTubePlayer = ({
  active,
  className,
  enabled,
  mode,
  media,
  seek,
  volume,
}) => {
  const modeClass = `src-youtube-Player--${mode}`;

  // Wrapper span so the backdrop can be full-size…
  return (

        {enabled && (
          <YouTubePlayerEmbed
            media={media}
            active={active}
            seek={Math.round(seek)}
            volume={volume}
            controllable={mode === 'preview'}
          />
        )}

  );
};

YouTubePlayer.propTypes = {
  className: PropTypes.string,
  mode: PropTypes.oneOf(['small', 'large', 'preview']),
  active: PropTypes.bool.isRequired,
  enabled: PropTypes.bool,
  media: PropTypes.object,
  seek: PropTypes.number,
  volume: PropTypes.number,
};

export default YouTubePlayer;

import cx from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';
import YouTubePlayerEmbed from './PlayerEmbed';

const YouTubePlayer = ({
  active,
  className,
  enabled,
  mode,
  media,
  seek,
  volume,
}) => {
  const modeClass = `src-youtube-Player--${mode}`;

  return (
    <div className={cx('src-youtube-Player', modeClass, className)} hidden={!active}>
      {enabled && (
        <YouTubePlayerEmbed
          media={media}
          active={active}
          seek={Math.round(seek)}
          volume={volume}
          controllable={mode === 'preview'}
        />
      )}
    </div>
  );
};

YouTubePlayer.propTypes = {
  className: PropTypes.string,
  mode: PropTypes.oneOf(['small', 'large', 'preview']),
  active: PropTypes.bool.isRequired,
  enabled: PropTypes.bool,
  media: PropTypes.object,
  seek: PropTypes.number,
  volume: PropTypes.number,
};

export default YouTubePlayer;

Remove unnecessary wrapper span from YouTube player.


angular.module("ngJustGage", [])
  .directive('justGage', ['$timeout', function ($timeout) {
    return {
      restrict: 'EA',
      scope: {
        id: '@',
        class: '@',
        min: '=',
        max: '=',
        title: '@',
        value: '='
      },
      template: '

',
      link: function (scope) {
        $timeout(function () {
          var graph = new JustGage({
            id: scope.id + '-justgage',
            min: scope.min,
            max: scope.max,
            title: scope.title,
            value: scope.value
          });

          scope.$watch('max', function (updatedMax) {
            if (updatedMax) {
              graph.refresh(scope.value, updatedMax);
            }
          }, true);

          scope.$watch('value', function (updatedValue) {
            if (updatedValue) {
              graph.refresh(updatedValue);
            }
          }, true);
        });
      }
    };
  }]);

angular.module("ngJustGage", [])
  .directive('justGage', ['$timeout', function ($timeout) {
    return {
      restrict: 'EA',
      scope: {
        id: '@',
        class: '@',
        min: '=',
        max: '=',
        title: '@',
        value: '=',
        options: '='
      },
      template: '

',
      link: function (scope,element,attrs) {
        $timeout(function () {
          var options = {
            id: scope.id + '-justgage',
            min: scope.min,
            max: scope.max,
            title: scope.title,
            value: scope.value
          }
          if ( scope.options ) {
              for (var key in scope.options) {
                  options[key]=scope.options[key];
              }
          }
          var graph = new JustGage(options);

          scope.$watch('max', function (updatedMax) {
            if (updatedMax) {
              graph.refresh(scope.value, updatedMax);
            }
          }, true);

          scope.$watch('value', function (updatedValue) {
            if (updatedValue) {
              graph.refresh(updatedValue);
            }
          }, true);
        });
      }
    };
  }]);

Add options attribute to pass a complete options list to directive


import React from 'react'
import {withReduxSaga} from 'configureStore'

import Layout from 'components/Layout'
import HomeHero from 'components/HomeHero'
import ProgramsList from 'containers/ProgramsList'
import SearchBar from 'containers/SearchBar'

class HomePage extends React.Component {
  render () {
    return (
      <Layout>

          <HomeHero />

            <SearchBar />
            <ProgramsList />

      </Layout>
    )
  }
}

export default withReduxSaga(HomePage)

import React from 'react'
import {withReduxSaga} from 'configureStore'

import Layout from 'components/Layout'
import HomeHero from 'components/HomeHero'
import ProgramsList from 'containers/ProgramsList'
import SearchBar from 'containers/SearchBar'

class HomePage extends React.Component {
  render () {
    return (
      <Layout>

          <HomeHero />

              Ce site en version Alpha est actuellement en développement actif. Merci de ne pas tenir compte des disfonctionnements pour le moment.

            <SearchBar />
            <ProgramsList />

      </Layout>
    )
  }
}

export default withReduxSaga(HomePage)

Add warning message on site alpha version


/**
 * Popup script
 */

'use strict';

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/app-component';

ReactDOM.render(<App/>, document.getElementById('styleme-app'));

/**
 * Popup script
 */

'use strict';

import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/app-component';

ReactDOM.render(<App/>, document.getElementById('styleme-app'));

Include babel-polyfill for using Promises


function sortProject(changeEvent) {

    var data = {};

    // Get old and new index from event
    data.oldIndex = changeEvent.oldIndex;
    data.newIndex = changeEvent.newIndex;

    // Get filters
    data.filters = {};
    data.filters = getCurrentFilters();

    // Get project id from event item
    data.projectId = $(changeEvent.item).children('input.cb-completed').val();

    console.log(data);

    // Make AJAX call to sort

    // TODO: Function to match index with task
    // TODO: Function to update sequences on update

    // TODO: make this queue-able
}

Clean up unfinished work + conflict


/*
Copyright 2018 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

module.exports = {
    plugins: {
        autoprefixer: {
            browsers: [
                "Android 2.3",
                "Android >= 4",
                "Chrome >= 20",
                "Firefox >= 24",
                "Explorer >= 8",
                "iOS >= 6",
                "Opera >= 12",
                "Safari >= 6"
            ]
        }
    },
}

/*
Copyright 2018 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

module.exports = {
    plugins: {
        autoprefixer: {}
    },
}

Remove browsers from PostCSS Config


Prism.languages.python= {
	'comment': {
		pattern: /(^|[^\\])#.*?(\r?\n|$)/g,
		lookbehind: true
	},
	'string': /"""[\s\S]+?"""|'''[\s\S]+?'''|("|')(\\?.)*?\1/g,
	'keyword' : /\b(as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/g,
	'boolean' : /\b(True|False)\b/g,
	'number' : /\b-?(0x)?\d*\.?[\da-f]+\b/g,
	'operator' : /[-+]{1,2}|=?<|=?>|!|={1,2}|(&){1,2}|(&){1,2}|\|?\||\?|\*|\/|~|\^|%|\b(or|and|not)\b/g,
	'ignore' : /&(lt|gt|amp);/gi,
	'punctuation' : /[{}[\];(),.:]/g
};

Fix numbers in Python (octal, binary, imaginary)


'use strict';

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

'use strict';

angular.module('sk.backgammon', [])
  .controller('MainCtrl', ['$scope', function ($scope) {
    $scope.stones = Array.apply(null, new Array(30)).map(function(e, i, arr) {
      return {
        player: (i % 2 ) ? 'black' : 'white',
        position: i
      }
    });
  }]);

Initialize array with 30 black and white stones/checkers.


import { api } from 'steem';

module.exports = api;

import { api } from 'steem';

api.setWebSocket('ws://138.201.198.167:8090');

module.exports = api;

Change WebSocket to @faddat one


/**
 * Module dependencies.
 */

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

// node executable
var node = process.execPath || process.argv[0];
var gnode = path.resolve(__dirname, '..', 'bin', 'gnode');
var mocha = path.resolve(__dirname, '..', 'node_modules', '.bin', 'mocha');
var mochaOpts = [ '--reporter', 'spec' ];

run([
  [ mocha, path.resolve(__dirname, 'cli.js') ]
]);

function run (tests) {
  if (0 == tests.length) return;
  var argv = tests.shift();
  if (argv.indexOf(mocha) != -1) {
    // running mocha, append "mochaOpts"
    argv.push.apply(argv, mochaOpts);
  }
  var opts = {
    customFds: [ 0, 1, 2 ],
    stdio: 'inherit'
  };
  var child = spawn(node, argv, opts);
  child.on('exit', function (code) {
    if (0 == code) {
      run(tests);
    } else {
      process.exit(code);
    }
  });
}

/**
 * Module dependencies.
 */

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

// node executable
var node = process.execPath || process.argv[0];
var mocha = path.resolve(__dirname, '..', 'node_modules', 'mocha', 'bin', 'mocha');
var mochaOpts = [ '--reporter', 'spec' ];

run([
  [ mocha, path.resolve(__dirname, 'cli.js') ]
]);

function run (tests) {
  if (0 == tests.length) return;
  var argv = tests.shift();
  if (argv.indexOf(mocha) != -1) {
    // running mocha, append "mochaOpts"
    argv.push.apply(argv, mochaOpts);
  }
  var opts = {
    customFds: [ 0, 1, 2 ],
    stdio: 'inherit'
  };
  var child = spawn(node, argv, opts);
  child.on('exit', function (code) {
    if (0 == code) {
      run(tests);
    } else {
      process.exit(code);
    }
  });
}

Fix `npm test` failing to run


// add two numbers without using +
function add(a,b) {
     if(b == 0)
         return a;
     var sum = a ^ b;
     var carry = (a & b) << 1;
     return add(sum,carry);
}

console.log('Sum of 2 & 5 is',add(2,5));

Add example invocation for operator override for +


(function() {
    'use strict';

    angular
        .module('app.layout')
        .controller('ShellController', ShellController);

    function ShellController(
        $rootScope,
        $scope,
        config,
        logger,
        coreevents,
        $sessionStorage,
        Users
    ) {
        var vm = this;
        vm.busyMessage = 'Please wait ...';
        vm.isBusy = true;
        vm.inHeader = true;
        vm.currentUser = null;

        activate();

        function activate() {
            logger.success(config.appTitle + ' loaded!', null);

            if ($sessionStorage.user) {
                setCurrentUser(null, $sessionStorage.user);
            }

            $rootScope.$on(coreevents.loginSuccess, setCurrentUser);
            $rootScope.$on(coreevents.logoutSuccess, unsetCurrentUser);
        }

        function setCurrentUser(event, user) {
            vm.currentUser = Users.initialize();
            vm.currentUser.new = false;
            vm.currentUser.stable = true;
            vm.currentUser.synchronized = true;
            vm.currentUser.data = user.data;

            vm.currentUser.refresh();

            $sessionStorage.user = user;
        }

        function unsetCurrentUser(event) {
            vm.currentUser = null;
            $sessionStorage.user = null;
            $scope.$apply();
        }
    }
})();

(function() {
    'use strict';

    angular
        .module('app.layout')
        .controller('ShellController', ShellController);

    function ShellController(
        $rootScope,
        $scope,
        config,
        logger,
        coreevents,
        $sessionStorage,
        Users
    ) {
        var vm = this;
        vm.busyMessage = 'Please wait ...';
        vm.isBusy = true;
        vm.inHeader = true;
        vm.currentUser = null;

        activate();

        function activate() {
            logger.success(config.appTitle + ' loaded!', null);

            if ($sessionStorage.user) {
                setCurrentUser(null, $sessionStorage.user);
            }

            $rootScope.$on(coreevents.loginSuccess, setCurrentUser);
            $rootScope.$on(coreevents.logoutSuccess, unsetCurrentUser);
        }

        function setCurrentUser(event, user) {
            vm.currentUser = Users.get(user.data.id);
            $sessionStorage.user = user;
        }

        function unsetCurrentUser(event) {
            vm.currentUser = null;
            $sessionStorage.user = null;
            $scope.$apply();
        }
    }
})();

Load initial user data via get