angular.module('ChillaxApp', [])
	.controller("ChillaxController", function () {

	var chillax = this;

    chillax.init = function() {
        chillax.nextNotification="Chillax loaded!";
        chillax.retrieveSettings();
    };

    chillax.retrieveSettings = function() {
        var settings = {};
        settings["enabled"] = "";
        settings["reminderInterval"] = "";
        settings["breakInterval"] = "";
        settings["sound"] = "";

        chillax.enabled = true;

        chrome.storage.sync.get(settings, function(obj) {

            console.log("Settings loaded: " + obj["enabled"]);

            chillax.reminderInterval = obj["reminderInterval"];
            chillax.breakInterval = settings["breakInterval"];
            chillax.sound = settings["sound"];
        });

    };

	chillax.playSound = function () {
		chillax.nextNotification="Is enabled: " + chillax.enabled;
        console.log("Play misty for me");

	};

	chillax.saveSettings = function () {
		console.log("Attempting to auto save the settings");

        // Prepare setting values to save
        var settings = {};
        settings["enabled"] = chillax.enabled;
        settings["reminderInterval"] = chillax.reminderInterval;
        settings["breakInterval"] = chillax.breakInterval;
        settings["sound"] = chillax.sound;

        // Output settings
        console.log(settings);

        chrome.storage.sync.set(settings, function() {
            console.log("Settings have been stored successfully");
        });

	};

});

angular.module('ChillaxApp', [])
	.controller("ChillaxController", function () {

	var chillax = this;

    chillax.init = function() {
        chillax.nextNotification="Chillax loaded!";
        chillax.retrieveSettings();
    };

    chillax.retrieveSettings = function() {
        var settings = {};
        settings["enabled"] = "";
        settings["reminderInterval"] = "";
        settings["breakInterval"] = "";
        settings["sound"] = "";

        chillax.enabled = true;

        chrome.storage.sync.get(settings, function(obj) {

            console.log("Settings loaded: " + obj["enabled"]);

            this.chillax.reminderInterval = obj["reminderInterval"];
            chillax.breakInterval = settings["breakInterval"];
            chillax.sound = settings["sound"];
        });

    };

	chillax.playSound = function () {
		chillax.nextNotification="Is enabled: " + chillax.enabled;
        console.log("Play misty for me");

	};

	chillax.saveSettings = function () {
		console.log("Attempting to auto save the settings");

        // Prepare setting values to save
        var settings = {};
        settings["enabled"] = chillax.enabled;
        settings["reminderInterval"] = chillax.reminderInterval;
        settings["breakInterval"] = chillax.breakInterval;
        settings["sound"] = chillax.sound;

        // Output settings
        console.log(settings);

        chrome.storage.sync.set(settings, function() {
            console.log("Settings have been stored successfully");
        });

	};

});

Resolve scope issues with save function callback


// We load the Safari fix before document-register-element because DRE
// overrides attachShadow() and calls back the one it finds on HTMLElement.
import './fix/safari';
import 'document-register-element/build/document-register-element';
import '@webcomponents/shadydom';
import '@webcomponents/shadycss';

// We load the Safari fix before document-register-element because DRE
// overrides attachShadow() and calls back the one it finds on HTMLElement.
import './fix/safari';
import 'document-register-element';
import '@webcomponents/shadydom';
import '@webcomponents/shadycss';

Revert "fix: Fix to import the browser version of document-register-element."


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

import {
  Router,
  Route,
  IndexRoute,
  browserHistory
} from 'react-router';

import './index.css';

import App from './App';
import { CoursesListPage } from './pages/courses-list/couses-list';
import { CoursesItemPage } from './pages/courses-item/courses-item';
import { CoursesNewPage } from './pages/courses-new/courses-new';

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

ReactDOM.render((
    <Router history={browserHistory}>
      <Route path="/" component={App}>

        <IndexRoute component={CoursesListPage}/>

        <Route path="/courses" component={CoursesListPage} />
        <Route path="/courses/new" component={CoursesNewPage}/>
        <Route path="/courses/:id" component={CoursesItemPage}/>

      </Route>
    </Router>
  ), document.getElementById('root')
);

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

import {
  Router,
  Route,
  IndexRoute,
  browserHistory
} from 'react-router';

import './index.css';

import App from './App';
import { CoursesListPage } from './pages/courses-list/couses-list';
import { CoursesItemPage } from './pages/courses-item/courses-item';
import { CoursesNewPage } from './pages/courses-new/courses-new';

ReactDOM.render((
    <Router history={browserHistory}>
      <Route path="/" component={App}>

        <IndexRoute component={CoursesListPage}/>

        <Route path="/courses" component={CoursesListPage} />
        <Route path="/courses/new" component={CoursesNewPage}/>
        <Route path="/courses/:id" component={CoursesItemPage}/>

      </Route>
    </Router>
  ), document.getElementById('root')
);

Delete init code (mount App)


define([
    "text!src/templates/file.html",
    "less!src/stylesheets/main.less"
], function(fileTemplate) {
    var _ = codebox.require("hr/utils");
    var commands = codebox.require("core/commands");
    var rpc = codebox.require("core/rpc");
    var dialogs = codebox.require("utils/dialogs");

    commands.register({
        id: "find.files",
        title: "Find: Browse Files",
        shortcuts: [
            "mod+p"
        ],
        run: function() {
            return dialogs.list(function(query) {
                return rpc.execute("find/files", {
                    query: query,
                    start: 0,
                    limit: 100
                })
                .get("results")
                .then(function(results) {
                    return _.map(results, function(result) {
                        return {
                            'path': result,
                            'filename': result.split('/').pop()
                        }
                    });
                });
            }, {
                template: fileTemplate,
                placeholder: "Browse Files"
            })
            .then(function(file) {
                return commands.run("file.open", {
                    'path': file.get("path")
                });
            });
        }
    });
});

define([
    "text!src/templates/file.html",
    "less!src/stylesheets/main.less"
], function(fileTemplate) {
    var _ = codebox.require("hr/utils");
    var commands = codebox.require("core/commands");
    var rpc = codebox.require("core/rpc");
    var dialogs = codebox.require("utils/dialogs");

    commands.register({
        id: "find.files",
        title: "Find: Jump to file",
        shortcuts: [
            "mod+p"
        ],
        run: function() {
            return dialogs.list(function(query) {
                return rpc.execute("find/files", {
                    query: query,
                    start: 0,
                    limit: 100
                })
                .get("results")
                .then(function(results) {
                    return _.map(results, function(result) {
                        return {
                            'path': result,
                            'filename': result.split('/').pop()
                        }
                    });
                });
            }, {
                template: fileTemplate,
                placeholder: "Jump to a file"
            })
            .then(function(file) {
                return commands.run("file.open", {
                    'path': file.get("path")
                });
            });
        }
    });
});

Change command title and placeholder


import { NetworkRack, User } from 'kinvey-js-sdk/dist/export';
import Kinvey from 'kinvey-html5-sdk';
import { HttpMiddleware } from './middleware';
import Popup from './popup';
import Push from './push';

// Setup racks
NetworkRack.useHttpMiddleware(new HttpMiddleware());

// Setup popup
User.usePopupClass(Popup);

// Add Push module to Kinvey
Kinvey.Push = Push;

// Export
module.exports = Kinvey;

import { NetworkRack } from 'kinvey-js-sdk/dist/export';
import { MobileIdentityConnect } from 'kinvey-js-sdk/dist/identity';
import Kinvey from 'kinvey-html5-sdk';
import { HttpMiddleware } from './middleware';
import Popup from './popup';
import Push from './push';

// Setup racks
NetworkRack.useHttpMiddleware(new HttpMiddleware());

// Setup popup
MobileIdentityConnect.usePopupClass(Popup);

// Add Push module to Kinvey
Kinvey.Push = Push;

// Export
module.exports = Kinvey;

Use the correct API to register the Popup class


export {configureStore, createDefaultMiddleware} from "./configureStore";

import createNextState from "immer";
import {combineReducers} from "redux";

export {
    createNextState,
    combineReducers,
};

export {configureStore, createDefaultMiddleware} from "./configureStore";

export {default as createNextState} from "immer";
export {combineReducers} from "redux";

Tweak re-export syntax for better output


module.exports = {
	package: {
		options: {
			cwd: '<%= paths.tmp %>',
			exclude: [
				'includes/back-compat\.php'
			],
			potHeaders: {
				'language': 'en',
				'plural-forms': 'nplurals=2; plural=(n != 1);',
				'poedit': true,
				'report-msgid-bugs-to': 'https://audiotheme.com/support/'
			},
			type: 'wp-theme'
		}
	}
};

module.exports = {
	package: {
		options: {
			cwd: '<%= paths.tmp %>',
			exclude: [
				'includes/back-compat\.php'
			],
			potHeaders: {
				'language': 'en',
				'plural-forms': 'nplurals=2; plural=(n != 1);',
				'poedit': true,
				'report-msgid-bugs-to': 'https://audiotheme.com/support/'
			},
			processPot: function( pot ) {
				var translation,
					excluded_meta = [
						'Theme Name of the plugin/theme',
						'Theme URI of the plugin/theme',
						'Author of the plugin/theme',
						'Author URI of the plugin/theme'
					];

				for ( translation in pot.translations[''] ) {
					if ( 'undefined' !== typeof pot.translations[''][ translation ].comments.extracted ) {
						if ( excluded_meta.indexOf( pot.translations[''][ translation ].comments.extracted ) >= 0 ) {
							console.log( 'Excluded meta: ' + pot.translations[''][ translation ].comments.extracted );
							delete pot.translations[''][ translation ];
						}
					}
				}

				return pot;
			},
			type: 'wp-theme'
		}
	}
};

Exclude some package meta from POT files.


import React, { PropTypes } from 'react'
import { Link } from 'react-router'
import styles from './index.module.css'

function Posts (props) {
  const posts = props.posts.map((post) => {
    const postRawDate = post.data.date
    const postDate = new Date(postRawDate).toDateString()

    return (

        <Link to={post.path}>

{post.data.title}

            <time className={styles.time} dateTime={postRawDate}>{postDate}</time>

      </Link>

    )
  })

  return (

        {posts}

  )
}

Posts.propTypes = {
  posts: PropTypes.array.isRequired
}

export default Posts

Fix linting issue with Posts


"use strict";

module.exports = listableOptions;

function listableOptions(yargs) {
  return yargs.options({
    json: {
      group: "Command Options:",
      describe: "Show information as a JSON array",
      type: "boolean",
    },
    ndjson: {
      group: "Command Options:",
      describe: "Show information as newline-delimited JSON",
      type: "boolean",
    },
    a: {
      group: "Command Options:",
      describe: "Show private packages that are normally hidden",
      type: "boolean",
      alias: "all",
    },
    l: {
      group: "Command Options:",
      describe: "Show extended information",
      type: "boolean",
      alias: "long",
    },
    p: {
      group: "Command Options:",
      describe: "Show parseable output instead of columnified view",
      type: "boolean",
      alias: "parseable",
    },
    toposort: {
      group: "Command Options:",
      describe: "Sort packages in topological order instead of lexical by directory",
      type: "boolean",
    },
    graph: {
      group: "Command Options:",
      describe: "Show dependency graph as a JSON-formatted adjacency list",
      type: "boolean",
    },
  });
}

"use strict";

module.exports = listableOptions;

function listableOptions(yargs, group = "Command Options:") {
  return yargs.options({
    json: {
      group,
      describe: "Show information as a JSON array",
      type: "boolean",
    },
    ndjson: {
      group,
      describe: "Show information as newline-delimited JSON",
      type: "boolean",
    },
    a: {
      group,
      describe: "Show private packages that are normally hidden",
      type: "boolean",
      alias: "all",
    },
    l: {
      group,
      describe: "Show extended information",
      type: "boolean",
      alias: "long",
    },
    p: {
      group,
      describe: "Show parseable output instead of columnified view",
      type: "boolean",
      alias: "parseable",
    },
    toposort: {
      group,
      describe: "Sort packages in topological order instead of lexical by directory",
      type: "boolean",
    },
    graph: {
      group,
      describe: "Show dependency graph as a JSON-formatted adjacency list",
      type: "boolean",
    },
  });
}

Allow override of options group name


//#!/usr/bin/env node

const cli = require("cli");
const uglify = require("uglify-es");

const readFile = require("../lib/read-file");
const generator = require("../lib/generator");

const classTranspiler = require("../");

const options = cli.parse({
    source: ["s", "Source class file path", "file", false],
    output: ["o", "Output file path", "file", false]
});

if (options.source === false) cli.fatal("Invalid or no source file.");
if (options.output === false) cli.fatal("Invalid or no output file.");

cli.info("Transpiling JS class");

generator(function*(cont) {
    cli.debug("Reading source file...");
    let source = yield readFile(options.source, cli.progress).then(cont());

    let result = classTranspiler.transpile(source);

    cli.debug("Uglifying code...");

    let ugly = uglify.minify(result);
    if (ugly.error) throw ugly.error;

    cli.debug("Saving code...");
    readFile.saveFile(options.output, ugly.code);

    cli.info("Completed. View the transpiled class at " + options.output);
})().catch(err => cli.fatal(err.stack));

//#!/usr/bin/env node

var cli = require("cli");
var uglify = require("uglify-es");

var readFile = require("../lib/read-file");
var generator = require("../lib/generator");

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

var options = cli.parse({
    source: ["s", "Source class file path", "file", false],
    output: ["o", "Output file path", "file", false]
});

if (options.source === false) cli.fatal("Invalid or no source file.");
if (options.output === false) cli.fatal("Invalid or no output file.");

cli.info("Transpiling JS class");

generator(function*(cont) {
    cli.debug("Reading source file...");
    var source = yield readFile(options.source, cli.progress).then(cont());

    var result = classTranspiler.transpile(source);

    cli.debug("Uglifying code...");

    var ugly = uglify.minify(result);
    if (ugly.error) throw ugly.error;

    cli.debug("Saving code...");
    readFile.saveFile(options.output, ugly.code);

    cli.info("Compvared. View the transpiled class at " + options.output);
})().catch(function(err) {cli.fatal(err.stack)});

Fix Windows Script Host error


/* eslint-env mocha, jest */

import getDefault from '../../shared/testUtils';
import NewIdeaDialog from '../NewIdeaDialog';

const defaultProps = {
  open: false,
  onRequestClose() {},
  onRequestAdd() {},
};
const getActualDialog = getDefault(NewIdeaDialog, defaultProps);

it('should render', () => {
  getActualDialog();
});
it('should be a <div />', () => {
  const wrapper = getActualDialog();
  expect(wrapper.is('div.NewIdeaDialog')).toBe(true);
});
it('should have a <Dialog />', () => {
  const wrapper = getActualDialog();
  expect(wrapper.find('Dialog').length).toBe(1);
});
it('should react to close request', (done) => {
  const wrapper = getActualDialog({
    onRequestClose() {
      done();
    },
  });
  wrapper.instance().props.onRequestClose();
});
it('should react to add request', (done) => {
  const wrapper = getActualDialog({
    onRequestAdd() {
      done();
    },
  });
  wrapper.instance().props.onRequestAdd();
});

it('should set FlatButton disabled based on state', () => {
  const wrapper = getActualDialog();
  wrapper.find('TextField').props().onChange({ target: { value: 'a cool idea' } });
  expect(wrapper.find('Dialog').prop('actions')[0].props.disabled).toBe(false);
  expect(wrapper.find('Dialog').prop('actions')[1].props.disabled).toBe(false);
});
it('should set TextField value based on state', () => {
  const wrapper = getActualDialog();
  wrapper.find('TextField').props().onChange({ target: { value: 'a cool idea' } });
  expect(wrapper.find('TextField').prop('value')).toBe('a cool idea');
});

Add NewIdeaDialog state unit tests


/* global console */
define( function () {

	'use strict';

	if ( typeof console !== undefined && typeof console.warn === 'function' && typeof console.warn.apply === 'function' ) {
		return function () {
			console.warn.apply( console, arguments );
		};
	}

	return function () {};

});

/* global console */
define( function () {

	'use strict';

	if ( typeof console !== 'undefined' && typeof console.warn === 'function' && typeof console.warn.apply === 'function' ) {
		return function () {
			console.warn.apply( console, arguments );
		};
	}

	return function () {};

});

Fix console error in IE


import React from 'react'
import { Text, View } from 'react-native'
import styled from 'styled-components/native'
import defaultTheme from './theme'

const LabelWrapper = styled.View`
  flex:.5;
  marginTop: ${props => props.inlineLabel ? 0 : 5};
`

const LabelText = styled.Text`
  color: ${props => props.theme.Label.color};
  font-size: ${props => props.theme.Label.fontSize};
  height: ${props => props.inlineLabel ? props.theme.FormGroup.height - props.theme.FormGroup.borderWidth*2 : props.theme.Label.stackedHeight};
  line-height: ${props => props.inlineLabel ? props.theme.FormGroup.height - props.theme.FormGroup.borderWidth*2 : props.theme.Label.stackedHeight};
`

LabelText.defaultProps = {
  theme: defaultTheme
}

const Label = props => {
  const { children, inlineLabel } = props

  return (
    <LabelWrapper inlineLabel={inlineLabel}>
      <LabelText inlineLabel={inlineLabel}>{ children }</LabelText>
    </LabelWrapper>
  )
}

Label.PropTypes = {
  children: React.PropTypes.string.isRequired
}

export default Label

import React from 'react'
import { Text, View } from 'react-native'
import styled from 'styled-components/native'
import defaultTheme from './theme'

const LabelWrapper = styled.View`
  marginTop: ${props => props.inlineLabel ? 0 : 5};
`

const LabelText = styled.Text`
  color: ${props => props.theme.Label.color};
  font-size: ${props => props.theme.Label.fontSize};
  height: ${props => props.inlineLabel ? props.theme.FormGroup.height - props.theme.FormGroup.borderWidth*2 : props.theme.Label.stackedHeight};
  line-height: ${props => props.inlineLabel ? props.theme.FormGroup.height - props.theme.FormGroup.borderWidth*2 : props.theme.Label.stackedHeight};
`

LabelText.defaultProps = {
  theme: defaultTheme
}

const Label = props => {
  const { children, inlineLabel } = props

  return (
    <LabelWrapper inlineLabel={inlineLabel}>
      <LabelText inlineLabel={inlineLabel}>{ children }</LabelText>
    </LabelWrapper>
  )
}

Label.PropTypes = {
  children: React.PropTypes.string.isRequired
}

export default Label

Fix alingment problem with multiline inputs


var assert = require('assert');
var domain = require('..');

describe('domain-regex', function() {

  // https://publicsuffix.org/list/effective_tld_names.dat
  it('should handle a wide array of domains', function() {
    var validDomains = ['some-example.construction', 'example.co.uk', 'example.aerodrome.aero', 'g.co'];
    validDomains.forEach(function(validDomain) {
      assert.equal(domain().test(validDomain), true);
    });
  });

  it('should not find a domain when it does not exist', function() {
    var invalidDomains = [
      'notvalid.com.',
      '.notvalid.com',
      'not_valid.com',
      'this.istoolongofatldrighthere.com',
      'thisiswaytoolongofatldoverherebecausethereisalimitof64thisiswaytoolongofatldoverherebecausethereisalimitof64.com'
    ];
    invalidDomains.forEach(function(invalidDomain) {
      assert.equal(domain().test(invalidDomain), false);
    });
  });
});

var assert = require('assert');
var domain = require('..');

describe('domain-regex', function() {

  // https://publicsuffix.org/list/effective_tld_names.dat
  it('should handle a wide array of domains', function() {
    var validDomains = ['some-example.construction', 'example.co.uk', 'example.aerodrome.aero', 'g.co'];
    validDomains.forEach(function(validDomain) {
      assert.equal(domain().test(validDomain), true);
    });
  });

  it('should not find a domain when it does not exist', function() {
    var invalidDomains = [
      'notvalid.com.',
      '.notvalid.com',
      'not_valid.com',
      '-notvalid.com',
      'notvalid-.com',
      'notvalid.com-',
      'this.istoolongofatldrighthere.com',
      'thisiswaytoolongofatldoverherebecausethereisalimitof64thisiswaytoolongofatldoverherebecausethereisalimitof64.com'
    ];
    invalidDomains.forEach(function(invalidDomain) {
      assert.equal(domain().test(invalidDomain), false);
    });
  });
});

Add a few '-' cases


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

test('Keyv is a function', t => {
	t.is(typeof Keyv, 'function');
});

test('Keyv cannot be invoked without \'new\'', t => {
	t.throws(() => Keyv()); // eslint-disable-line new-cap
	t.notThrows(() => new Keyv());
});

test('Keyv is an instance of Keyv', t => {
	t.true(new Keyv() instanceof Keyv);
});

test('.set(key, value) returns a Promise', t => {
	const store = new Keyv();
	t.true(store.set('foo', 'bar') instanceof Promise);
});

test('.set(key, value) resolves to value', async t => {
	const store = new Keyv();
	t.is(await store.set('foo', 'bar'), 'bar');
});

test('.get(key) returns a Promise', t => {
	const store = new Keyv();
	t.true(store.get('foo') instanceof Promise);
});

test('.get(key) resolves to value', async t => {
	const store = new Keyv();
	await store.set('foo', 'bar');
	t.is(await store.get('foo'), 'bar');
});

Test .get(key) resolves to value


/**
 * Listens for the app launching then creates the window
 *
 * @see http://developer.chrome.com/trunk/apps/app.runtime.html
 * @see http://developer.chrome.com/trunk/apps/app.window.html
 */
chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create('browser.html', {
    'width': 1024,
    'height': 768
  });
});

/**
 * Listens for the app launching then creates the window
 *
 * @see http://developer.chrome.com/trunk/apps/app.runtime.html
 * @see http://developer.chrome.com/trunk/apps/app.window.html
 */
chrome.app.runtime.onLaunched.addListener(function() {
  runApp();
});

/**
 * Listens for the app restarting then re-creates the window.
 *
 * @see http://developer.chrome.com/trunk/apps/app.runtime.html
 */
chrome.app.runtime.onRestarted.addListener(function() {
  runApp();
});

/**
 * Creates the window for the application.
 *
 * @see http://developer.chrome.com/trunk/apps/app.window.html
 */
function runApp() {
  chrome.app.window.create('browser.html', {
    'width': 1024,
    'height': 768
  });
}

Add onRestarted listener, and restart app: crbug/167196, crbug/155221


/*global describe, it */
"use strict";
(function () {

	describe( "$.fn.paginate()", function () {

		it( "should be defined", function() {
			expect( $.fn.paginate ).to.be.a( "function" );
		});

		it( "should validate attributes.resource", function() {
			[ undefined, null, 23, function() {} ].forEach(function( unvalidType ) {
				expect(function() {
					$( "<div>" ).paginate([{
						resource: unvalidType,
						pagination: {}
					}]);
				}).to.throw( Error, /unexpected attributes.resource/ );
			});
		});

	});

})();

/*global describe, it */
"use strict";
(function () {

	describe( "$.fn.paginate()", function () {

		var DummyResource = function( attributes ) {
			attributes = attributes || { flavor: "success" };
			this.get = DummyResource.prototype.flavors[ attributes.flavor ];
		};
		DummyResource.prototype = {};
		DummyResource.prototype.flavors = {
			success: function() {
				return $.Deferred().resolve({ data: [] });
			},
			failure: function() {
				return $.Deferred().reject();
			}
		};

		it( "should be defined", function() {
			expect( $.fn.paginate ).to.be.a( "function" );
		});

		it( "should validate attributes.resource", function() {
			[ undefined, null, 23, function() {} ].forEach(function( unvalidType ) {
				expect(function() {
					$( "<div>" ).paginate([{
						resource: unvalidType,
						pagination: {}
					}]);
				}).to.throw( Error, /unexpected attributes.resource/ );
			});
		});

		it( "should emit \"success\" event on fetch success", function( done ) {
			this.timeout( 1000 );
			$( "<div>" ).paginate([{
				resource: new DummyResource(),
				pagination: { type: "index-offset" }
			}]).on( "success", function() {
				done();
			}).trigger( "next" );
		});

		it( "should emit \"error\" event on fetch failure", function( done ) {
			this.timeout( 1000 );
			$( "<div>" ).paginate([{
				resource: new DummyResource({ flavor: "failure" }),
				pagination: { type: "index-offset" }
			}]).on( "error", function() {
				done();
			}).trigger( "next" );
		});

	});

})();

Add success and error events tests


'use strict';

/**
 *
 * @param data the result that came out from the history api getting recently (last 10) consumers conversations
 */
function getAgentIdForBestConversationMCS (data) {
    let conversations = data.conversationHistoryRecords;
    let mcsArr = [];

    conversations.forEach((conversation) => {
        if (conversation && conversation.info && conversation.info.mcs) {
            mcsArr.push(
                {
                    "mcs": conversation.info.mcs,
                    "agentId": conversation.info.latestAgentId
                });
        }
    });

    mcsArr.sort((a, b) => a.mcs - b.mcs);
    return mcsArr && mcsArr.length > 0 && mcsArr.pop().agentId;
}

module.exports = {
    getAgentIdForBestConversationMCS
};

'use strict';

/**
 *
 * @param data the result that came out from the history api getting recently (last 10) consumers conversations
 */
function getAgentDataForBestConversationMCS (data) {
    let conversations = data.conversationHistoryRecords;
    let mcsArr = [];

    conversations.forEach((conversation) => {
        if (conversation && conversation.info && conversation.info.mcs) {
            mcsArr.push(
                {
                    'mcs': conversation.info.mcs,
                    'agentId': conversation.info.latestAgentId,
                    'skillId': conversation.info.latestSkillId
                });
        }
    });

    mcsArr.sort((a, b) => a.mcs - b.mcs);
    return mcsArr && mcsArr.length > 0 && mcsArr.pop();
}

module.exports = {
    getAgentDataForBestConversationMCS
};

Return the agent id and the skill id


import React from 'react';

const Projects = props => {
  return (

      {Object.keys(props.projects).map(projectId => {
        const ticket = props.projects[projectId][0];
        return (

            {ticket.company.name} —
            <button
              type="button"
              className="btn-link"
              onClick={props.searchProject.bind(this, {
                company: ticket.company.name,
                project: ticket.project.name,
              })}
            >
              {ticket.project.name}
            </button>

        );
      })}

  );
};

export default Projects;

import React from 'react';

const Projects = props => {
  return (

      {Object.keys(props.projects).map(projectId => {
        const ticket = props.projects[projectId][0];
        return (
          <button
            key={projectId}
            type="button"
            className="list-group-item"
            onClick={props.searchProject.bind(this, {
              company: ticket.company.name,
              project: ticket.project.name,
            })}
          >
            {ticket.company.name} — {ticket.project.name}
          </button>
        );
      })}

  );
};

export default Projects;

Move projects dropdown to Bootstrap list-group


// This file is part of Indico.
// Copyright (C) 2002 - 2019 CERN
//
// Indico is free software; you can redistribute it and/or
// modify it under the terms of the MIT License; see the
// LICENSE file for more details.

import 'jquery-ui';
import 'jquery-ui/ui/effect';
import 'jquery-ui/ui/effects/effect-pulsate';
import 'jquery-ui/ui/effects/effect-highlight';
import 'jquery-ui/ui/widgets/datepicker';
import 'jquery-ui/ui/widgets/dialog';
import 'jquery-ui/ui/widgets/draggable';
import 'jquery-ui/ui/widgets/droppable';
import 'jquery-ui/ui/widgets/resizable';
import 'jquery-ui/ui/widgets/sortable';
import 'jquery-ui/ui/widgets/slider';
import 'jquery-ui/ui/widgets/tabs';

import 'jquery-ui/themes/base/all.css';

// This file is part of Indico.
// Copyright (C) 2002 - 2019 CERN
//
// Indico is free software; you can redistribute it and/or
// modify it under the terms of the MIT License; see the
// LICENSE file for more details.

import 'jquery-ui';
import 'jquery-ui/ui/effect';
import 'jquery-ui/ui/effects/effect-pulsate';
import 'jquery-ui/ui/effects/effect-highlight';
import 'jquery-ui/ui/effects/effect-blind';
import 'jquery-ui/ui/widgets/datepicker';
import 'jquery-ui/ui/widgets/dialog';
import 'jquery-ui/ui/widgets/draggable';
import 'jquery-ui/ui/widgets/droppable';
import 'jquery-ui/ui/widgets/resizable';
import 'jquery-ui/ui/widgets/sortable';
import 'jquery-ui/ui/widgets/slider';
import 'jquery-ui/ui/widgets/tabs';

import 'jquery-ui/themes/base/all.css';

Fix broken comment field in CfP


import { Subscriptions } from './collection';
import { subscriptionTypes } from './schema'
import { runCallbacksAsync, newMutation } from 'meteor/vulcan:core';
import Users from 'meteor/vulcan:users';

export const defaultSubscriptionTypeTable = {
  "Posts": subscriptionTypes.newComments,
  "Users": subscriptionTypes.newPosts,
  "Localgroups": subscriptionTypes.newEvents,
}

/**
 * @summary Perform the un/subscription after verification: update the collection item & the user
 * @param {String} action
 * @param {Collection} collection
 * @param {String} itemId
 * @param {Object} user: current user (xxx: legacy, to replace with this.userId)
 * @returns {Boolean}
 */
export const performSubscriptionAction = async (action, collection, itemId, user) => {
  const collectionName = collection.options.collectionName
  const newSubscription = {
    state: action === "subscribe" ? 'subscribed' : 'supressed',
    documentId: itemId,
    collectionName,
    deleted: false,
    type: defaultSubscriptionTypeTable[collectionName]
  }
  newMutation({
    collection: Subscriptions,
    document: newSubscription,
    validate: true,
    currentUser: user,
    context: {
      currentUser: user,
      Users: Users,
    },
  })

  if (action === 'subscribe') {
    await runCallbacksAsync('users.subscribe.async', action, collection, itemId, user);
  } else {
    await runCallbacksAsync('users.unsubscribe.async', action, collection, itemId, user);
  }
};

import { Subscriptions } from './collection';
import { subscriptionTypes } from './schema'
import { runCallbacksAsync, newMutation } from 'meteor/vulcan:core';
import Users from 'meteor/vulcan:users';

export const defaultSubscriptionTypeTable = {
  "Comments": subscriptionTypes.newReplies,
  "Posts": subscriptionTypes.newComments,
  "Users": subscriptionTypes.newPosts,
  "Localgroups": subscriptionTypes.newEvents,
}

/**
 * @summary Perform the un/subscription after verification: update the collection item & the user
 * @param {String} action
 * @param {Collection} collection
 * @param {String} itemId
 * @param {Object} user: current user (xxx: legacy, to replace with this.userId)
 * @returns {Boolean}
 */
export const performSubscriptionAction = async (action, collection, itemId, user) => {
  const collectionName = collection.options.collectionName
  const newSubscription = {
    state: action === "subscribe" ? 'subscribed' : 'supressed',
    documentId: itemId,
    collectionName,
    type: defaultSubscriptionTypeTable[collectionName]
  }
  newMutation({
    collection: Subscriptions,
    document: newSubscription,
    validate: true,
    currentUser: user,
    context: {
      currentUser: user,
      Users: Users,
    },
  })

  if (action === 'subscribe') {
    await runCallbacksAsync('users.subscribe.async', action, collection, itemId, user);
  } else {
    await runCallbacksAsync('users.unsubscribe.async', action, collection, itemId, user);
  }
};

Fix two crashes in performSubscriptionAction


export const getViewport = () => ({
  height: window.innerHeight || document.documentElement.offsetHeight,
  width: window.innerWidth || document.documentElement.offsetWidth,
});

export const isElementOverflowing = (element) => element.clientWidth < element.scrollWidth || element.clientHeight < element.scrollHeight;

Add util function to detect if an element is overflowing


// EXPRESS ROUTES

const routes = require('express').Router();
const logger = require('../lib/logger')

routes.get('/game/:gameid', (req, res) => {
    //todo: check if the appropriate game id has been logged
    res.status(200).json({ "game_id": req.params.gameid, status: false});
});

module.exports = routes;

// EXPRESS ROUTES

const routes = require('express').Router();
const logger = require('../lib/logger')

routes.get('/game/:gameid', (req, res) => {
    //todo: check if the appropriate game id has been logged
    if(req.params.gameid == 'bad'){
        res.status(400).json({ error: true });
    } else {
        res.status(200).json({ game_id: req.params.gameid, error: false });
    }
});

module.exports = routes;

Add a good and bad endpoint ... quick sanity checks


// @description Number unit tests
assert(3.0 == 3, "Number equality");
assert("3" == 3, "Number and string equality");
assert("4" !== 3, "Number and string strict equality");
assert(Number.isFinite(2), "Number.isFinite");
assert(Number.isFinite(Infinity) === false, "Number.isFinite");
assert((5 + 2) == 7, "Number addition");
assert((5 - 2) == 3, "Number subtraction");
assert((5 / 2) == 2.5, "Number division");
assert((5 * 2) == 10, "Number multiplication");
assert((3 + 4 * 5) == 23, "Operator precedence");

Add operator presedence test in tests


var FilterService   = require("services/ItemFilterService");
var ResourceService = require("services/ResourceService");

Vue.component("item-filter", {

    props: [
        "template",
        "facet",
        "categoryId"
    ],

    data: function()
    {
        return {
            facetParams: []
        };
    },

    created: function()
    {
        this.$options.template = this.template || "#vue-item-filter";
        ResourceService.bind("facetParams", this);
    },

    computed:
    {
        selectedNames: function()
        {
            if (this.facetParams.length > 0)
            {
                var names = [];

                for (var selectedFacetId in this.facetParams)
                {
                    for (var facetIndex in this.facet.values)
                    {
                        if (this.facet.values[facetIndex].id == this.facetParams[selectedFacetId] &&
                             names.indexOf(this.facet.values[facetIndex].names[0].name) == -1)
                        {
                            names.push(this.facet.values[facetIndex].names[0].name);
                        }
                    }
                }

                return names;
            }

            return "";
        }
    },

    methods:
    {
        updateFacet: function()
        {
            FilterService.applyFacets(this.facetParams, this.categoryId);
        }
    }
});

var FilterService   = require("services/ItemFilterService");
var ResourceService = require("services/ResourceService");

Vue.component("item-filter", {

    props: [
        "template",
        "facet",
        "categoryId"
    ],

    data: function()
    {
        return {
            facetParams: []
        };
    },

    created: function()
    {
        this.$options.template = this.template || "#vue-item-filter";
        ResourceService.bind("facetParams", this);
    },

    methods:
    {
        updateFacet: function()
        {
            FilterService.applyFacets(this.facetParams, this.categoryId);
        }
    }
});

REMOVE unused computed property in ItemFIlter


/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @format
 * @flow strict-local
 */

'use strict';

import type DraftEntityInstance from 'DraftEntityInstance';
import type {OrderedMap} from 'immutable';
// TODO: when removing the deprecated API update this to use the EntityMap type
// instead of OrderedMap

let key = 0;

function addEntityToEntityMap(
  entityMap: OrderedMap<*, *>,
  instance: DraftEntityInstance,
): OrderedMap<*, *> {
  return entityMap.set(`${++key}`, instance);
}

module.exports = addEntityToEntityMap;

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @format
 * @flow
 */

'use strict';

import type DraftEntityInstance from 'DraftEntityInstance';
import type {OrderedMap} from 'immutable';
// TODO: when removing the deprecated API update this to use the EntityMap type
// instead of OrderedMap

let key = 0;

function addEntityToEntityMap(
  entityMap: OrderedMap<*, *>,
  instance: DraftEntityInstance,
): OrderedMap<*, *> {
  return entityMap.set(`${++key}`, instance);
}

module.exports = addEntityToEntityMap;

Enable deprecated-type rule in www


import React, { Component } from 'react'
import Helmet from 'react-helmet'

import { NavBar } from '../../components'

const styles = {
  iframe: {
    width: '100%',
  }
}

class TeamPage extends Component {
  componentDidMount() {
    var iframe = this.refs.iframe

    iframe.addEventListener('load', () => {
      var iframeScrollHeight = iframe.contentWindow.document.body.scrollHeight + 'px'

      iframe.height = iframeScrollHeight
    })
  }

  render() {
    return(
      <div>
        <Helmet title="Team" />
        <NavBar />

        <iframe ref="iframe" style={styles.iframe} src="/staticPage/team/index.html" />
      </div>
    )
  }
}

export default TeamPage

import React, { Component } from 'react'
import Helmet from 'react-helmet'

import { NavBar } from '../../components'

const styles = {
  iframe: {
    width: '100%',
  }
}

class TeamPage extends Component {
  componentDidMount() {
    var iframe = this.refs.iframe

    window.addEventListener('resize', () => {
      var iframeScrollHeight = iframe.contentWindow.document.body.scrollHeight + 'px'

      iframe.height = iframeScrollHeight
    })

    iframe.addEventListener('load', () => {
      var iframeScrollHeight = iframe.contentWindow.document.body.scrollHeight + 'px'

      iframe.height = iframeScrollHeight
    })
  }

  render() {
    return(
      <div>
        <Helmet title="Team" />
        <NavBar />

        <iframe ref="iframe" style={styles.iframe} src="/staticPage/team/index.html" />
      </div>
    )
  }
}

export default TeamPage

Fix resizing issues with iframed pages


var app = angular.module("browseApp.routes", ['ui.router']);

app.config(function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider

    .otherwise('/categories');

    $stateProvider

    .state('allcats',{
        url: '/categories',
        templateUrl: 'templates/catlist.html',
        controller: 'header_c'
    })

    .state('list',{
        url: '/list',
        templateUrl: 'templates/list.html',
        controller: 'header_l'
    })

    .state('new',{
        url: '/new',
        templateUrl: 'templates/new.html',
        controller: 'header_n'
    });

});

var app = angular.module("browseApp.routes", ['ui.router']);

app.config(function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider

    .otherwise('/categories');

    $stateProvider

    .state('allcats',{
        url: '/categories',
        templateUrl: 'templates/catlist.html',
        controller: 'header_c'
    })

    .state('list',{
        url: '/list/:catID',
        templateUrl: 'templates/list.html',
        controller: 'header_l'
    })

    .state('new',{
        url: '/new/:catID',
        templateUrl: 'templates/new.html',
        controller: 'header_n'
    });

});

Add catID url parameter to state


const Signal = require('signals')

function SpotLight (opts) {
  this.type = 'SpotLight'
  this.enabled = true
  this.changed = new Signal()
  this.target = [0, 0, 0]
  this.color = [1, 1, 1, 1]
  this.intensity = 1
  this.angle = Math.PI / 4
  this.innerAngle = Math.PI / 6
  this.range = 10
  this.castShadows = false

  this.set(opts)
}

SpotLight.prototype.init = function (entity) {
  this.entity = entity
}

SpotLight.prototype.set = function (opts) {
  Object.assign(this, opts)

  if (opts.color !== undefined || opts.intensity !== undefined) {
    this.color[3] = this.intensity
  }

  Object.keys(opts).forEach((prop) => this.changed.dispatch(prop))
}

module.exports = function (opts) {
  return new SpotLight(opts)
}

const Signal = require('signals')

function SpotLight (opts) {
  this.type = 'SpotLight'
  this.enabled = true
  this.changed = new Signal()
  this.target = [0, 0, 0]
  this.color = [1, 1, 1, 1]
  this.intensity = 1
  this.angle = Math.PI / 4
  this.innerAngle = 0
  this.range = 10
  this.castShadows = false

  this.set(opts)
}

SpotLight.prototype.init = function (entity) {
  this.entity = entity
}

SpotLight.prototype.set = function (opts) {
  Object.assign(this, opts)

  if (opts.color !== undefined || opts.intensity !== undefined) {
    this.color[3] = this.intensity
  }

  Object.keys(opts).forEach((prop) => this.changed.dispatch(prop))
}

module.exports = function (opts) {
  return new SpotLight(opts)
}

Set spotlight inner angle to glTF defaults


import path from 'path'
import fs from 'fs'
import HtmlWebpackPlugin from 'html-webpack-plugin'

module.exports = {
  entry: './app.js',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
    inline: true,
    historyApiFallback: true
  },
  module: {
    loaders: [ {
      test: /\.js$/,
      loaders: [ 'babel' ],
      exclude: /node_modules/,
      include: __dirname
    } ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'index.html', // Load a custom template
      inject: 'body' // Inject all scripts into the body
    })
  ]
}

// This will make the redux-auth-wrapper module resolve to the
// latest src instead of using it from npm. Remove this if running
// outside of the source.
const src = path.join(__dirname, '..', '..', 'src')
if (fs.existsSync(src)) {
  // Use the latest src
  module.exports.resolve = { alias: { 'redux-auth-wrapper': src } }
  module.exports.module.loaders.push({
    test: /\.js$/,
    loaders: [ 'babel' ],
    include: src
  })
}

import path from 'path'
import fs from 'fs'
import HtmlWebpackPlugin from 'html-webpack-plugin'

module.exports = {
  entry: './app.js',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
    inline: true,
    historyApiFallback: true,
    stats: {
      colors: true,
      hash: false,
      version: false,
      chunks: false,
      children: false
    }
  },
  module: {
    loaders: [ {
      test: /\.js$/,
      loaders: [ 'babel' ],
      exclude: /node_modules/,
      include: __dirname
    } ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'index.html', // Load a custom template
      inject: 'body' // Inject all scripts into the body
    })
  ]
}

// This will make the redux-auth-wrapper module resolve to the
// latest src instead of using it from npm. Remove this if running
// outside of the source.
const src = path.join(__dirname, '..', '..', 'src')
if (fs.existsSync(src)) {
  // Use the latest src
  module.exports.resolve = { alias: { 'redux-auth-wrapper': src } }
  module.exports.module.loaders.push({
    test: /\.js$/,
    loaders: [ 'babel' ],
    include: src
  })
}

Make the example webpack build less noisy


$(document).ready(function() {

	// Show/hide elements depending on window size
	if ($(window).width() <= 1024) {
		$("nav").hide();
	} else if ($(window).width() > 1024) {
		$("nav").show();
		$(".nav-btn").hide();
	};

	// Toggle for the dropdown
	$(".dropbtn").click(function () {
		$("nav").slideToggle();
		$(this).toggleClass("transform");
	});
});

$(document).ready(function(){

	// Toggle for dropdown
	$(".dropbtn").click(function () {
		$("nav").slideToggle();
		$(this).toggleClass("transform");
		$("header").toggleClass("headerbg");
	});

    // Get window scroll position
    var scrollPosition = $(window).scrollTop();

    // Function to add background if scrolled
    function checkScroll(){
        var startY = $("header").height() + 10;
        if($(window).scrollTop() > startY) {
            $("header").addClass("scrolled");
        } else {
            $("header").removeClass("scrolled");
        }
    }
    if($("header").length > 0) {
        $(window).on("scroll load resize", function(){
            checkScroll();
        });
    }

    // If scroll postion is not at the top then call function
    if(scrollPosition > 25){
        checkScroll();
    }

    function showNav() {
    	if ($(window).width() >= 1024) {
    		$("nav").show();
    	} else {
    		$("nav").hide();
    	}
    }

    window.onresize = showNav;
});

Rewrite JavaScript for website redesign


Array.prototype.sortBy = function(key){
  var get = Ember.get;

  return this.sort(function(a, b){
    return get(a, key) > get(b, key);
  });
};

Array.prototype.sum = function(){
  var total = 0;
  this.forEach(function(val){
    total += val;
  });
  return total;
};

Array.prototype.toInts = function(){
  return this.map(function(val){
    return parseInt(val, 10);
  });
};

Ember.Enumerable.reopen({
  toInts: Array.prototype.toInts,
  sum: Array.prototype.sum,
  sortBy: Array.prototype.sortBy
});

Function.prototype.debounce = function(time){
  var func = this;

  return function(){
    Em.run.debounce(this, func, time);
  };
};

Array.prototype.sortBy = function(key){
  var get = Ember.get;

  return this.sort(function(a, b){
    return get(a, key) > get(b, key);
  });
};

Array.prototype.sum = function(){
  return this.reduce(function(a, b){
    return a + b;
  });
};

Array.prototype.toInts = function(){
  return this.map(function(val){
    return parseInt(val, 10);
  });
};

Ember.Enumerable.reopen({
  toInts: Array.prototype.toInts,
  sum: Array.prototype.sum,
  sortBy: Array.prototype.sortBy
});

Function.prototype.debounce = function(time){
  var func = this;

  return function(){
    Em.run.debounce(this, func, time);
  };
};

Use [].reduce instead of [].forEach :D


import { inject, bindable, bindingMode } from 'aurelia-framework';
import { Config } from 'aurelia-api';

@inject(Element, Config)
export class UiAutocomplete {
    @bindable({defaultBindingMode: bindingMode.twoWay}) value;
    @bindable from;
    @bindable placeholder;
    @bindable searchParams = {};
    @bindable displayValue = 'name';
    @bindable storeValue = 'id';
    @bindable defaultText = '';
    @bindable multiple = false;

    constructor(element, config) {
        this.element = element;
        this.endpoint = config.getEndpoint('api');

        this.doQuery = (settings, callback) => {
            let path = `${this.from}/`;
            let params = this.searchParams;
            params.search = settings.urlData.query;
            this.endpoint.find(path, params).then(data => {
                callback(data);
            });
        }

        this.updateFromDropdown = (value, text, choice) => {
            this.value = value;
        }
    }

    attached() {
        setTimeout(() => {
            this.dropdown = $('.search.selection.dropdown', this.element).dropdown({
                apiSettings: {
                    responseAsync: this.doQuery,
                },
                fields: {
                    remoteValues: 'results',
                    name: this.displayValue,
                    value: this.storeValue,
                },
                onChange: this.updateFromDropdown,
                localSearch: false,
            });
            this.dropdown.dropdown('set text', this.defaultText);
        }, 1);
    }
}

import { inject, bindable, bindingMode } from 'aurelia-framework';
import { Config } from 'aurelia-api';

@inject(Element, Config)
export class UiAutocomplete {
    @bindable({defaultBindingMode: bindingMode.twoWay}) value;
    @bindable from;
    @bindable placeholder;
    @bindable searchParams = {};
    @bindable displayValue = 'name';
    @bindable storeValue = 'id';
    @bindable defaultText = '';
    @bindable multiple = false;

    constructor(element, config) {
        this.element = element;
        this.endpoint = config.getEndpoint('api');

        this.doQuery = (settings, callback) => {
            let path = `${this.from}/`;
            let params = this.searchParams;
            params.search = settings.urlData.query;
            this.endpoint.find(path, params).then(data => {
                callback(data);
            });
        }

        this.updateFromDropdown = (value, text, choice) => {
            this.value = value;
        }
    }

    defaultTextChanged(value) {
        if (this.dropdown) {
            this.dropdown.dropdown('set text', this.defaultText);
        }
    }

    attached() {
        setTimeout(() => {
            this.dropdown = $('.search.selection.dropdown', this.element).dropdown({
                apiSettings: {
                    responseAsync: this.doQuery,
                },
                fields: {
                    remoteValues: 'results',
                    name: this.displayValue,
                    value: this.storeValue,
                },
                onChange: this.updateFromDropdown,
                localSearch: false,
            });
            this.dropdown.dropdown('set text', this.defaultText);
        }, 1);
    }
}

Fix missing default values with autocomplete


import authenticable from './composers/authenticable';
import searchable from './composers/searchable';

const debug = require('debug')('popura:factory');

/**
 * Factory function that composes an instance of popura
 *
 * @param  {string} username = ''
 * @param  {string} password = ''
 * @return {object} - popura instance
 */
export default function popuraFactory(username = '', password = '') {
	debug(
		`New mal client user '${username}'. Use password = ${Boolean(password)}`
	);

	const state = {
		username,
		password,
		authToken: '',
	};

	return Object.assign({},
		authenticable(state),
		searchable(state)
	);
}

import authenticable from './composers/authenticable';
import searchable from './composers/searchable';
import listable from './composers/listable';

const debug = require('debug')('popura:factory');

/**
 * Factory function that composes an instance of popura
 *
 * @param  {string} username = ''
 * @param  {string} password = ''
 * @return {object} - popura instance
 */
export default function popuraFactory(username = '', password = '') {
	debug(
		`New mal client user '${username}'. Use password = ${Boolean(password)}`
	);

	const state = {
		username,
		password,
		authToken: '',
	};

	return Object.assign({},
		authenticable(state),
		searchable(state),
		listable(state)
	);
}

Add listable composer to popuraFactory


(function() {
'use strict';

// Inject SVG icons into the DOM
var element = document.createElement('div');
element.id = 'darkroom-icons';
element.style = 'height: 0; width: 0; position: absolute; visibility: hidden';
element.innerHTML = '<!-- inject:svg --><!-- endinject -->';
document.body.appendChild(element);

})();

(function() {
'use strict';

// Inject SVG icons into the DOM
var element = document.createElement('div');
element.id = 'darkroom-icons';
element.style.height = 0;
element.style.width = 0;
element.style.position = 'absolute';
element.style.visibility = 'hidden';
element.innerHTML = '<!-- inject:svg --><!-- endinject -->';
document.body.appendChild(element);

})();

Fix write on a read-only property


let Config = require('../../config');
let url = require('url');
let webHook = url.resolve(Config.app.url, '/codeship/');

class FormatterService {

	format(type, build) {
		return defaultFormat(build)
	}

	getStartMessage(chatId) {
		let hook = this.getWebHook(chatId);
		return `${FormatterService.EMOJI.ship} Add this URL to your Codeship notification settings:
				${hook}
				@codeship_bot by @dominic0`;
	}

	getWebHook(chatId) {
		return url.resolve(webHook, `${chatId}`);
	}

}

FormatterService.EMOJI = {
	ship: '\u{1F6A2}',
	success: '\u{2705}',
	error: '\u{274C}'
};

function defaultFormat(build) {
	return `${FormatterService.EMOJI.ship} ${build.project_name}: ${build.message}
			on ${build.branch} - ${build.status} ${FormatterService.EMOJI[build.status] || ''}
			Open on Codeship`;
}

module.exports = new FormatterService();

let Config = require('../../config');
let url = require('url');
let webHook = url.resolve(Config.app.url, '/codeship/');

class FormatterService {

	format(type, build) {
		return defaultFormat(build)
	}

	getStartMessage(chatId) {
		let hook = this.getWebHook(chatId);
		return `${FormatterService.EMOJI.ship} Hi! I see you want to receive Codeship notifications.
				Just add this URL as a Webhook to your Codeship notification settings to receive notifications in this conversation.

				To receive only failing builds (and the recovering builds)
				${hook}

				To receive all builds (succeeding and failing)
				${hook}?mode=all

				@codeship_bot by @dominic0`;
	}

	getWebHook(chatId) {
		return url.resolve(webHook, `${chatId}`);
	}

}

FormatterService.EMOJI = {
	ship: '\u{1F6A2}',
	success: '\u{2705}',
	error: '\u{274C}'
};

function defaultFormat(build) {
	return `${FormatterService.EMOJI.ship} ${build.project_name} - ${build.branch} ${FormatterService.EMOJI[build.status] || ''}
 			${build.committer}: ${build.message}
			Open on Codeship`;
}

module.exports = new FormatterService();

Update format of welcome message and notification


import React, { Component } from 'react';
import { Link } from 'react-router';
import Endpoints from './Endpoints';
import './styles/HeaderBoardList.css';

class HeaderBoardList extends Component {
  componentDidMount() { this.fetchBoards(); }
  componentWillReceiveProps() { this.fetchBoards(); }

  render() {
    let abbreviations = [];
    if (this.state && this.state.data) {
      abbreviations = this.state.data.map(b => b.abbreviation).sort();
    }
    const list = abbreviations.map(abbr => <HeaderBoardListItem abbr={abbr} key={abbr} />);
    return (

          {list}

    );
  }

  fetchBoards() {
    Endpoints.Boards().getData()
    .then(data => {
      this.setState({data: data});
    })
    .catch(err => {
      console.error("HeaderBoardList::fetchBoards", "Couldn't get Boards endpoint!", err);
    })
  }
}

const HeaderBoardListItem = ({abbr}) => (

    <Link className='HeaderBoardList__list__item__anchor' to={'/boards/' + abbr + '/'}>
      {abbr}
    </Link>

);

export default HeaderBoardList;

import React, { Component } from 'react';
import { Link } from 'react-router';
import Endpoints from './Endpoints';
import './styles/HeaderBoardList.css';

class HeaderBoardList extends Component {
  componentDidMount() { this.fetchBoards(); }
  componentWillReceiveProps() { this.fetchBoards(); }

  render() {
    let abbreviations = [];
    if (this.state && this.state.data) {
      abbreviations = this.state.data.map(d => d.board.abbreviation).sort();
    }
    const list = abbreviations.map(abbr => <HeaderBoardListItem abbr={abbr} key={abbr} />);
    return (

          {list}

    );
  }

  fetchBoards() {
    Endpoints.Boards().getData()
    .then(data => {
      this.setState({data: data});
    })
    .catch(err => {
      console.error("HeaderBoardList::fetchBoards", "Couldn't get Boards endpoint!", err);
    })
  }
}

const HeaderBoardListItem = ({abbr}) => (

    <Link className='HeaderBoardList__list__item__anchor' to={'/boards/' + abbr + '/'}>
      {abbr}
    </Link>

);

export default HeaderBoardList;

Fix abbreviation list not showing


import translations from './translations/accessibility-cloud.js-widget/translations';

function findLocaleWithCountry(localeWithoutCountry) {
  const regexp = new RegExp(`^${localeWithoutCountry}_`);
  return Object.keys(translations).find(locale => locale.match(regexp));
}

function translate(string, locale) {
  const localeWithoutCountry = locale.replace(/_[A-Z][A-Z]$/);
  const localeHasCountry = locale !== localeWithoutCountry;
  const localeWithDefaultCountry = localeHasCountry ? locale : findLocaleWithCountry(locale);

  const translation = translations[locale] || {};
  const translationWithoutCountry = translations[localeWithoutCountry] || {};
  const translationWithDefaultCountry = translations[localeWithDefaultCountry] || {};

  return translation[string] ||
    translationWithoutCountry[string] ||
    translationWithDefaultCountry[string] ||
    string;
}

// Note that we don't support template strings for now.

// eslint-disable-next-line import/prefer-default-export
export function t(arg) {
  // eslint-disable-next-line no-undef
  return translate(arg, AccessibilityCloud.locale);
}

import translations from './translations/accessibility-cloud.js-widget/translations';

function findLocaleWithCountry(localeWithoutCountry) {
  const regexp = new RegExp(`^${localeWithoutCountry}_`);
  return Object.keys(translations).find(locale => locale.match(regexp));
}

function translate(string, locale) {
  const localeWithoutCountry = locale.replace(/_[A-Z][A-Z]$/);
  const localeHasCountry = locale !== localeWithoutCountry;
  const localeWithDefaultCountry = localeHasCountry ? locale : findLocaleWithCountry(locale);
  const defaultLocale = 'en_US';

  const translation = translations[locale] || {};
  const translationWithoutCountry = translations[localeWithoutCountry] || {};
  const translationWithDefaultCountry = translations[localeWithDefaultCountry] || {};
  const translationWithDefaultLocale = translations[defaultLocale] || {};

  return translation[string] ||
    translationWithoutCountry[string] ||
    translationWithDefaultCountry[string] ||
    translationWithDefaultLocale ||
    string;
}

// Note that we don't support template strings for now.

// eslint-disable-next-line import/prefer-default-export
export function t(arg) {
  // eslint-disable-next-line no-undef
  return translate(arg, AccessibilityCloud.locale);
}

Add en_US translation as fallback


define('mygame', ['lyria/game', 'lyria/localization/group', 'mygame/achievements', 'mygame/scenelist', 'mygame/prefablist', 'mygame/assetlist', 'mygame/i18n', 'lyria/prefab/manager'], function(Game, LocalizationGroup, achievements, sceneList, prefabList, assetList, i18n, PrefabManager) {'use strict';

  // Create a new game object
  var myGame = new Game();

  myGame.localization = new LocalizationGroup(i18n);

  // Set up achievements
  achievements(myGame.viewport, myGame.localization.achievements);

  console.log(myGame);

  // Set generated scene files
  myGame.director.scenes = sceneList();

  // Set generated prefab files
  PrefabManager.prefabs = prefabList();

  // myGame allows to add scenes directly, which internally uses the scene director
  myGame
    // Add "scene1"
    .addScene('scene1')
    // Add "scene2"
    .addScene('scene2');

  // If preloader is complete, everything in this function happens
  myGame.preloader.on('complete', function() {
    // Show "scene1"
    myGame.showScene('scene1');
  });

  // Set asset list for preloader
  myGame.preloader.assets = assetList;

  // Spin up the preloader
  myGame.preloader.start();

  return myGame;
});

define(['require'], function(require) {'use strict';

  // Loading all necessary modules
  var Game = require('lyria/game');
  var LocalizationGroup = require('lyria/localization/group');
  var achievements = require('mygame/achievements');
  var sceneList = require('mygame/scenelist');
  var prefabList = require('mygame/prefablist');
  var assetList = require('mygame/assetlist');
  var i18n = require('mygame/i18n');
  var PrefabManager = require('lyria/prefab/manager');

  // Create a new game object
  var myGame = new Game();

  myGame.localization = new LocalizationGroup(i18n);

  // Set up achievements
  achievements(myGame.viewport, myGame.localization.achievements);

  console.log(myGame);

  // Set generated scene files
  myGame.director.scenes = sceneList();

  // Set generated prefab files
  PrefabManager.prefabs = prefabList();

  // myGame allows to add scenes directly, which internally uses the scene director
  myGame
    // Add "scene1"
    .addScene('scene1')
    // Add "scene2"
    .addScene('scene2');

  // If preloader is complete, everything in this function happens
  myGame.preloader.on('complete', function() {
    // Show "scene1"
    myGame.showScene('scene1');
  });

  // Set asset list for preloader
  myGame.preloader.assets = assetList;

  // Spin up the preloader
  myGame.preloader.start();

  return myGame;
});

Update loading of modules in main entry point


'use strict';

// Modify models
const upsert = function ({ collection, newData }) {
  const func = newData.length === 1 ? upsertOne : upsertMany;
  return func({ collection, newData });
};

const upsertOne = function ({ collection, newData: [data] }) {
  const { _id: id } = data;
  return collection.upsertOne({ _id: id }, data);
};

const upsertMany = async function ({ collection, newData }) {
  const bulkCommands = newData
    .map(replacement => getBulkCommand({ replacement }));
  const result = await collection.bulkWrite(bulkCommands);
  return { result };
};

const getBulkCommand = function ({ replacement }) {
  const { _id: id } = replacement;
  return { replaceOne: { filter: { _id: id }, replacement, upsert: true } };
};

module.exports = {
  upsert,
};

'use strict';

// Modify models
const upsert = function ({ collection, newData }) {
  const func = newData.length === 1 ? upsertOne : upsertMany;
  return func({ collection, newData });
};

const upsertOne = function ({ collection, newData: [data] }) {
  const { _id: id } = data;
  return collection.replaceOne({ _id: id }, data, { upsert: true });
};

const upsertMany = async function ({ collection, newData }) {
  const bulkCommands = newData
    .map(replacement => getBulkCommand({ replacement }));
  const result = await collection.bulkWrite(bulkCommands);
  return { result };
};

const getBulkCommand = function ({ replacement }) {
  const { _id: id } = replacement;
  return { replaceOne: { filter: { _id: id }, replacement, upsert: true } };
};

module.exports = {
  upsert,
};

Fix wrong MongoDB driver method name


#!/usr/bin/env node

const
  fs      = require('fs')
  program = require('commander');

program
  .version('0.0.1')
  .description('A command line script to minify files.')
  .option('-f, --file <file>', 'Specify the file to minify: minify -f thisFile.js')
  .parse(process.argv);

if (program.file) {
  /* Read the file into a string */
  var filePath       = program.file
    , fileContent    = fs.readFileSync(filePath, 'utf8')
    , minFileContent = minifyString(fileContent)
    , minFileName    = addMinExtension(filePath);

  var minifiedFile = fs.writeFile(minFileName, minFileContent, function(err) {
    if (err) { throw err; };
    console.log('-- ' + filePath + ' minified to ' + minFileName + ' --');
  });
};

/***************************************/
//         Helper Functions            //
/***************************************/

/* Remove all white space from str. */
function minifyString(str) {
  return str.replace(/\s/g, '');
};

/* Insert .min extension to FileName. */
function addMinExtension(FileName) {
  var fileExtIndex = FileName.indexOf('.')
    , filePrefix   = FileName.slice(0, fileExtIndex)
    , fileExten    = FileName.slice(fileExtIndex, FileName.length)
    , minFileName  = filePrefix + '.min' + fileExten;

  return minFileName;
};

#!/usr/bin/env node

const
  fs      = require('fs')
  colours = require('colors')
  program = require('commander');

program
  .version('0.0.1')
  .description('A command line script to minify files.')
  .option('-f, --file <file>', 'Specify the file to minify: minify -f thisFile.js')
  .parse(process.argv);

if (program.file) {
  /* Read the file into a string */
  var filePath       = program.file
    , fileContent    = fs.readFileSync(filePath, 'utf8')
    , minFileContent = minifyString(fileContent)
    , minFileName    = addMinExtension(filePath);

  var minifiedFile = fs.writeFile(minFileName, minFileContent, function(err) {
    if (err) {
      console.log(filePath.blue.underline + ' minify unsuccessful'.red);
      throw err;
    };
    console.log(filePath.blue.underline + ' minified successfully to '.green + minFileName.blue.underline);
  });
};

/***************************************/
//         Helper Functions            //
/***************************************/

/* Remove all white space from str. */
function minifyString(str) {
  return str.replace(/\s/g, '');
};

/* Insert .min extension to FileName. */
function addMinExtension(fileName) {
  var fileExtIndex = fileName.indexOf('.')
    , filePrefix   = fileName.slice(0, fileExtIndex)
    , fileExten    = fileName.slice(fileExtIndex, fileName.length)
    , minFileName  = filePrefix + '.min' + fileExten;

  return minFileName;
};

Add colour to execution status.


import Ember from 'ember';

export default Ember.Component.extend({
  siteUrl: null,
  file: null,
  fileName: null,
  message: '',
  isSaving: false,
  actions:{
    startComplete: function(){
      var siteUrl = this.get('siteUrl');
      var file = this.get('file');
      if(!siteUrl && !file){
        this.set('message', 'Please provide a URL.');
      } else {
        this.set('message', '');
        this.sendAction('action', siteUrl, file);
      }
    },
    goToSecondStep: function() {
      let controller = this.get('parentView.parentView.controller');
      controller.setActiveStep(controller.get('steps')[1].step);
    }
  }
});

import Ember from 'ember';

var isValidUrl = function(siteUrl) {
  return /^(http|https):\/\/[^ "]+$/.test(siteUrl);
};

export default Ember.Component.extend({
  siteUrl: null,
  file: null,
  fileName: null,
  message: '',
  isSaving: false,
  actions:{
    startComplete: function(){
      var siteUrl = this.get('siteUrl');
      var file = this.get('file');
      if(!siteUrl && !file || !isValidUrl(siteUrl)){
        this.set('message', 'Please provide a URL.');
      } else {
        this.set('message', '');
        this.sendAction('action', siteUrl, file);
      }
    },
    goToSecondStep: function() {
      let controller = this.get('parentView.parentView.controller');
      controller.setActiveStep(controller.get('steps')[1].step);
    }
  }
});

Add url validation on frontend


var React = require("react");
var renderToDOMNode = require("../render-to-dom-node.js");
var ReagicEditor = require("./reagic-editor.js");

class EditorExample extends ReagicEditor {
    renderEditor(){
        return <input onChange={() => this.onChange()} ref="input"></input>
    }
    readData(value){
        var newData = React.findDOMNode(this.refs.input).value;
        return newData;
    }
}

describe("ReagicEditor", function(){
    var data,
        domNode,
        onChange,
        schema;

    beforeEach(function(){
        data = "hi";
        schema = {
            title: "Greeting",
            validators: [
                function(value){
                    return value !== "";
                }
            ]
        };

        this.renderDomNodeWithData = function(data){
            return renderToDOMNode(
                <EditorExample data={data} schema={schema} onChange={function(){
                    onChange.apply(this, arguments);
                }}/>
            );
        }

        domNode = this.renderDomNodeWithData(data);
    });

    it("Renders the label with the field title", function(){
        expect(domNode.querySelector("label").innerHTML).toContain(schema.title);
    });

    it("Correctly shows that a field without any validators is valid", function(){
        expect(domNode.querySelector("label").innerHTML).toContain("Looks good");
    });

    it("Shows 'Fix this' in indicator if a value isn't valid", function(){
        domNode = this.renderDomNodeWithData("");
        expect(domNode.querySelector("label").innerHTML).toContain("Fix this");
    });
})

var React = require("react");
var renderToDOMNode = require("../render-to-dom-node.js");
var ReagicEditor = require("./reagic-editor.js");

class EditorExample extends ReagicEditor {
    renderEditor(){
        return <input onChange={() => this.onChange()} ref="input"></input>
    }
    readData(value){
        var newData = React.findDOMNode(this.refs.input).value;
        return newData;
    }
}

describe("ReagicEditor", function(){
    var data,
        domNode,
        onChange,
        schema;

    beforeEach(function(){
        data = "hi";
        schema = {
            title: "Greeting",
            validators: [
                function(value){
                    return value !== "";
                }
            ]
        };

        this.renderDomNodeWithData = function(data){
            return renderToDOMNode(
                <EditorExample data={data} schema={schema} onChange={function(){
                    onChange.apply(this, arguments);
                }}/>
            );
        }

        domNode = this.renderDomNodeWithData(data);
    });

    it("Renders the label with the field title", function(){
        expect(domNode.querySelector("label").innerHTML).toContain(schema.title);
    });

    it("Correctly shows that a field without any validators is valid", function(){
        expect(domNode.querySelector("label").innerHTML).toContain("✔");
    });

    it("Shows 'Fix this' in indicator if a value isn't valid", function(){
        domNode = this.renderDomNodeWithData("");
        expect(domNode.querySelector("label").innerHTML).toContain("Fix this");
    });
})

Fix "Looks good" unit test.


import React from 'react';
import LinkButton from 'shared/components/linkButton/linkButton';
import Donate from 'shared/components/donate/donate';
import Join from 'shared/components/join/join';
import WhatWeDo from './whatWeDo/whatWeDo';
import Membership from './membership/membership';
import MoreInformation from './moreInformation/moreInformation';
import Partners from './partners/partners';
import SuccessStories from './successStories/successStories';
import styles from './landing.css';

const Landing = () => (

The largest community dedicated to helping military veterans and
        families launch software development careers.

      <LinkButton text="Join" theme="red" link="/signup" />

    <WhatWeDo />
    <Membership />
    <MoreInformation />
    <SuccessStories />
    <Partners />
    <Donate />
    <Join />

);

export default Landing;

import React from 'react';
import LinkButton from 'shared/components/linkButton/linkButton';
import AnnounceBanner from 'shared/components/announceBanner/announceBanner';
import WhatWeDo from './whatWeDo/whatWeDo';
import Membership from './membership/membership';
import MoreInformation from './moreInformation/moreInformation';
import SuccessStories from './successStories/successStories';
import Partners from './partners/partners';
import Donate from '../../../shared/components/donate/donate';
import Join from '../../../shared/components/join/join';
import BenefitBanner from '../../../images/benefit.jpg';
import MobileBenefitBanner from '../../../images/benefit-mobile.jpg';
import styles from './landing.css';

const Landing = () => (

        The largest community dedicated to helping military veterans and families launch software
        development careers.

      <LinkButton text="Join" theme="red" link="/signup" />

    <AnnounceBanner
      link="/benefit"
      imageSource={BenefitBanner}
      fallbackImage450pxWideSource={MobileBenefitBanner}
      altText="Click here to find more information about our Benefit Dinner and Silent Auction on November 11th"
    />
    <WhatWeDo />
    <Membership />
    <MoreInformation />
    <SuccessStories />
    <Partners />
    <Donate />
    <Join />

);

export default Landing;

Implement Announce Banner for /benefit


var log = require('../lib/logger');

var allTheSettings = {
  coinbaseApiKey: process.env.COINBASE_API_KEY,
  coinbaseApiSec: process.env.COINBASE_API_SECRET,
  coinbaseApiUri: process.env.COINBASE_API_URI,
  coinbaseApiWallet: process.env.COINBASE_API_WALLET,
  dbDb: process.env.DATABASE_DB,
  dbHost: process.env.DATABASE_HOST,
  dbPass: process.env.DATABASE_PASS,
  dbUrl: process.env.DATABASE_URL,
  dbUser: process.env.DATABASE_USER,
  redisUrl: process.env.REDIS_URL,
  secret: process.env.SECRET,
  mailerUser: process.env.MG_USER,
  mailerPass: process.env.MG_PASS
};

try {
  var overrides = require('./configOverrides.js');

  log.debug("Found overrides for", Object.keys(overrides));

  for (var prop in overrides) {
    if (overrides.hasOwnProperty(prop)) {
      if (allTheSettings[prop]) {
        allTheSettings[prop] = overrides[prop];
      }
    }
  }
} catch (e) {
  // if no override file is found
  log.debug("no overrides found, reverting to env vars");

}

module.exports = allTheSettings;

var log = require('../lib/logger');

var allTheSettings = {
  coinbaseApiKey: process.env.COINBASE_API_KEY,
  coinbaseApiSec: process.env.COINBASE_API_SECRET,
  coinbaseApiUri: process.env.COINBASE_API_URI,
  coinbaseApiWallet: process.env.COINBASE_API_WALLET,
  dbDb: process.env.DATABASE_DB,
  dbHost: process.env.DATABASE_HOST,
  dbPass: process.env.DATABASE_PASS,
  dbUrl: process.env.DATABASE_URL,
  dbUser: process.env.DATABASE_USER,
  redisUrl: process.env.REDIS_URL,
  secret: process.env.SECRET,
  mailerUser: process.env.MG_USER,
  mailerPass: process.env.MG_PASS,
  authSecret: process.env.AUTH_SECRET
};

try {
  var overrides = require('./configOverrides.js');

  log.debug("Found overrides for", Object.keys(overrides));

  for (var prop in overrides) {
    if (overrides.hasOwnProperty(prop)) {
      if (allTheSettings[prop]) {
        allTheSettings[prop] = overrides[prop];
      }
    }
  }
} catch (e) {
  // if no override file is found
  log.debug("no overrides found, reverting to env vars");

}

module.exports = allTheSettings;

Add authSecret to config vars


var nconf = require('nconf');

nconf
  .file({ file: './config/config.json' })
  .env();

nconf.defaults({
  'RIPPLE_REST_API': 'https://localhost:5990',
  'DATABASE_URL': 'postgres://postgres:password@localhost:5432/ripple_gateway',
  'RIPPLE_DATAMODEL_ADAPTER': 'ripple-gateway-data-sequelize-adapter',
  'RIPPLE_EXPRESS_GATEWAY': 'ripple-gateway-express',
  'SSL': false,
  'PORT': 3000
});

module.exports = nconf;

var nconf = require('nconf');

nconf
  .file({ file: './config/config.json' })
  .env();

nconf.defaults({
  'RIPPLE_REST_API': 'http://localhost:5990',
  'DATABASE_URL': 'postgres://postgres:password@localhost:5432/ripple_gateway',
  'RIPPLE_DATAMODEL_ADAPTER': 'ripple-gateway-data-sequelize-adapter',
  'RIPPLE_EXPRESS_GATEWAY': 'ripple-gateway-express',
  'SSL': false,
  'PORT': 3000
});

module.exports = nconf;

Remove ssh from ripple rest api endpoint.


'use strict'
var db = require('../app/db');

exports.up = function(next) {
  db.Semester.create([
  	{
  		name: 'סתיז תשע"ז', year: 2017, semester: 1,
  		startDate: new Date("2016-10-29T00:00:00.000Z"),
  		endDate: new Date("2017-01-26T00:00:00.000Z"),
  		examsStart: new Date("2017-01-28T00:00:00.000Z"),
  		examsEnd: new Date("2017-03-09T00:00:00.000Z")
  	},
	{
	    name: 'אביב תשע"ז',  year: 2017, semester: 2,
	    startDate: new Date("2017-03-11T00:00:00.000Z"),
  		endDate: new Date("2017-06-29T00:00:00.000Z"),
  		examsStart: new Date("2017-07-01T00:00:00.000Z"),
	}
  	], function(err) {
	    if (err)
	      throw err;
	    next();
	  });
};

exports.down = function(next) {
  next();
};

'use strict'
var db = require('../app/db');

exports.up = function(next) {
  db.Semester.create([
  	{
  		name: 'סתיו תשע"ז', year: 2017, semester: 1,
  		startDate: new Date("2016-10-29T00:00:00.000Z"),
  		endDate: new Date("2017-01-26T00:00:00.000Z"),
  		examsStart: new Date("2017-01-28T00:00:00.000Z"),
  		examsEnd: new Date("2017-03-09T00:00:00.000Z")
  	},
	{
	    name: 'אביב תשע"ז',  year: 2017, semester: 2,
	    startDate: new Date("2017-03-11T00:00:00.000Z"),
  		endDate: new Date("2017-06-29T00:00:00.000Z"),
  		examsStart: new Date("2017-07-01T00:00:00.000Z"),
	}
  	], function(err) {
	    if (err)
	      throw err;
	    next();
	  });
};

exports.down = function(next) {
  next();
};

Fix typo in migration. Didn't bother with new migration, nothing was ever deployed


import Submitter from './Submitter'
import SensorsAnalytics from './SensorsAnalytics'

export { Submitter }
export default SensorsAnalytics

import Submitter from './Submitter'
import SensorsAnalytics from './SensorsAnalytics'

// Use module.exports to ensure compatiblity with non-es6 projects
exports = module.exports = SensorsAnalytics
exports.Submitter = Submitter

Use module.exports to ensure compatibility


{
  angular.module('marvelousnote.signUp')
    .directive('signUp', [

      '$state',
      'UsersService',
      ($state, UsersService) => {

        class SignUpController {
          constructor() {
            this.user = {};
          }
          submit() {
            UsersService.create(this.user)
              .then(
                () => $state.go('notes.form', { noteId: undefined })
              );
          }
        }

        return {
          scope: {},
          controller: SignUpController,
          controllerAs: 'vm',
          bindToController: true,
          templateUrl: 'sign-up/sign-up.html',
        };
      }
    ]);
}

{
  angular.module('marvelousnote.signUp')
  .directive('signUp', [

    '$state',
    'Flash',
    'UsersService',
    ($state, Flash, UsersService) => {

      let flash = false;
      class SignUpController {
        constructor() {
          this.user = {};
        }
        submit() {
          if (Number.isInteger(flash)) {
            Flash.dismiss(flash);
            flash = false;
          }
          UsersService.create(this.user)
          .then(
            () => $state.go('notes.form', { noteId: undefined }),

            (res) => {
              let errors = '';
              for (let error of res.data.errors) {
                errors += `<li>${error}</li>`;
              }
              flash = Flash.create('danger', `

Oops! Something went wrong.

${errors}

              `);
            }
          );
        }
      }

      return {
        scope: {},
        controller: SignUpController,
        controllerAs: 'vm',
        bindToController: true,
        templateUrl: 'sign-up/sign-up.html',
      };
    }
  ]);
}

Use a relative path for sign-up template.


if (typeof AFRAME === 'undefined') {
  throw new Error('Component attempted to register before AFRAME was available.');
}

// Components
require('./components/motion-capture-recorder.js');
require('./components/motion-capture-replayer.js');
require('./components/avatar-recorder.js');
require('./components/avatar-replayer.js');
require('./components/stroke.js');

// Systems
require('./systems/motion-capture-recorder.js');
require('./systems/motion-capture-replayer.js');

if (typeof AFRAME === 'undefined') {
  throw new Error('Component attempted to register before AFRAME was available.');
}

// Components
require('./components/motion-capture-recorder.js');
require('./components/motion-capture-replayer.js');
require('./components/avatar-recorder.js');
require('./components/avatar-replayer.js');
require('./components/stroke.js');

// Systems
require('./systems/motion-capture-replayer.js');

Remove reference to removed module


'use strict'

var StyleSheet = require('./StyleSheet')
var Rule = require('./Rule')
var PluginsRegistry = require('./PluginsRegistry')

/**
 * Jss constructor.
 *
 * @api public
 */
function Jss() {
	this.plugins = new PluginsRegistry()
}

module.exports = Jss

/**
 * Creates a new instance of Jss.
 *
 * @see Jss
 * @api public
 */
Jss.prototype.create = function () {
	return new Jss()
}

/**
 * Create a stylesheet.
 *
 * @see StyleSheet
 * @api public
 */
Jss.prototype.createStyleSheet = function (rules, options) {
    if (!options) options = {}
    options.jss = this
    var sheet = new StyleSheet(rules, options)
    return sheet
}

/**
 * Create a rule.
 *
 * @see Rule
 * @return {Rule}
 * @api public
 */
Jss.prototype.createRule = function (selector, style, options) {
    if (typeof selector == 'object') {
        options = style
        style = selector
        selector = null
    }
    if (!options) options = {}
    options.jss = this
    var rule = new Rule(selector, style, options)
    this.plugins.run(rule)
    return rule
}

/**
 * Register plugin. Passed function will be invoked with a rule instance.
 *
 * @param {Function} fn
 * @api public
 */
Jss.prototype.use = function (fn) {
	this.plugins.use(fn)
}

'use strict'

var StyleSheet = require('./StyleSheet')
var Rule = require('./Rule')
var PluginsRegistry = require('./PluginsRegistry')

/**
 * Jss constructor.
 *
 * @api public
 */
function Jss() {
	this.plugins = new PluginsRegistry()
}

module.exports = Jss

/**
 * Creates a new instance of Jss.
 *
 * @see Jss
 * @api public
 */
Jss.prototype.create = function () {
	return new Jss()
}

/**
 * Create a stylesheet.
 *
 * @see StyleSheet
 * @api public
 */
Jss.prototype.createStyleSheet = function (rules, options) {
    if (!options) options = {}
    options.jss = this
    var sheet = new StyleSheet(rules, options)
    return sheet
}

/**
 * Create a rule.
 *
 * @see Rule
 * @return {Rule}
 * @api public
 */
Jss.prototype.createRule = function (selector, style, options) {
    if (typeof selector == 'object') {
        options = style
        style = selector
        selector = null
    }
    if (!options) options = {}
    options.jss = this
    var rule = new Rule(selector, style, options)
    this.plugins.run(rule)
    return rule
}

/**
 * Register plugin. Passed function will be invoked with a rule instance.
 *
 * @param {Function} fn
 * @api public
 */
Jss.prototype.use = function (fn) {
	this.plugins.use(fn)
	return this
}

Return the instance from jss.use()


var genericLoader = require('./generic_report_loader');

var AGGREGATION_STEPS = [
  { $project: { instanceId: 1, date: 1, _id: 0 } },
  { $group: { _id: "$instanceId",  activityDates: { $push: "$date"} } },
  { $project: {  instanceId: "$_id",  _id: 0, activityDates: 1 } }
];

function wrapResults(results) {
  return {
    stats: results
  };
}

module.exports = {
  load: function instancesPerDay(db) {
    return genericLoader.makeMongoAggregation(db, AGGREGATION_STEPS).then(wrapResults);
  }
};

var genericLoader = require('./generic_report_loader');

var AGGREGATION_STEPS = [
  { $project: { instanceId: 1, date: 1, active: 1, _id: 0 } },
  { $group: { _id: "$instanceId",  activityDates: { $push: { date: "$date", active: "$active" } } } },
  { $project: {  instanceId: "$_id",  _id: 0, activityDates: 1 } }
];

function wrapResults(results) {
  return {
    stats: results
  };
}

module.exports = {
  load: function instancesPerDay(db) {
    return genericLoader.makeMongoAggregation(db, AGGREGATION_STEPS).then(wrapResults);
  }
};

Change lifetime report to include active/inactive status


import { expect } from 'chai'
import { describe, it } from 'mocha'
import { setupComponentTest } from 'ember-mocha'
import hbs from 'htmlbars-inline-precompile'
import { find } from 'ember-native-dom-helpers'

describe('Integration | Component | no permission', function() {
  setupComponentTest('no-permission', {
    integration: true
  })

  it('renders', function() {
    this.render(hbs`{{no-permission}}`)

    expect(find('.empty')).to.have.length(1)
    expect(find('.empty').innerHTML).to.contain('Halt')
  })
})

import { expect } from 'chai'
import { describe, it } from 'mocha'
import { setupComponentTest } from 'ember-mocha'
import hbs from 'htmlbars-inline-precompile'
import { find } from 'ember-native-dom-helpers'

describe('Integration | Component | no permission', function() {
  setupComponentTest('no-permission', {
    integration: true
  })

  it('renders', function() {
    this.render(hbs`{{no-permission}}`)

    expect(find('.empty')).to.be.ok
    expect(find('.empty').innerHTML).to.contain('Halt')
  })
})

Fix test for no-permission component


var ssr = require("../lib/");
var helpers = require("./helpers");
var assert = require("assert");
var iterate = require("leakage").iterate;
var path = require("path");
var through = require("through2");

describe("Memory leaks", function(){
	this.timeout(10000);

	before(function(){
		this.oldXHR = global.XMLHttpRequest;
		global.XMLHttpRequest = helpers.mockXHR(
			'[ { "a": "a" }, { "b": "b" } ]');

		var render = ssr({
			config: "file:" + path.join(__dirname, "tests", "package.json!npm"),
			main: "async/index.stache!done-autorender"
		});

		this.render = function(pth){
			return new Promise(function(resolve, reject){
				var stream = through(function(buffer){
					resolve(buffer);
				});
				stream.on("error", reject);
				render(pth).pipe(stream);
			});
		};
	});

	after(function(){
		global.XMLHttpRequest = this.oldXHR;
	});

	it("do not happen", function(done){
		iterate(10, () => {
			return this.render("/");
		})
		.then(() => {
			done();
		})
		.catch(done);
	});
});

/* jshint esversion: 6 */
var ssr = require("../lib/");
var helpers = require("./helpers");
var iterate = require("leakage").iterate;
var path = require("path");
var through = require("through2");

describe("Memory leaks", function(){
	this.timeout(30000);

	before(function(){
		this.oldXHR = global.XMLHttpRequest;
		global.XMLHttpRequest = helpers.mockXHR(
			'[ { "a": "a" }, { "b": "b" } ]');

		var render = ssr({
			config: "file:" + path.join(__dirname, "tests", "package.json!npm"),
			main: "async/index.stache!done-autorender"
		});

		this.render = function(pth){
			return new Promise(function(resolve, reject){
				var stream = through(function(buffer){
					resolve(buffer);
				});
				stream.on("error", reject);
				render(pth).pipe(stream);
			});
		};
	});

	after(function(){
		global.XMLHttpRequest = this.oldXHR;
	});

	it("do not happen", function(done){
		iterate(10, () => {
			return this.render("/");
		})
		.then(() => {
			done();
		})
		.catch(done);
	});
});

Increase the leak test timeout for CI


/**
 * @module timed
 * @submodule timed-components
 * @public
 */
import Component from '@ember/component'
import moment from 'moment'
import Ember from 'ember'
import { task, timeout } from 'ember-concurrency'

const { testing } = Ember

export default Component.extend({
  classNames: ['timed-clock'],

  hour: 0,
  minute: 0,
  second: 0,

  _update() {
    let now = moment()

    let second = now.seconds() * 6
    let minute = now.minutes() * 6 + second / 60
    let hour = (now.hours() % 12) / 12 * 360 + 90 + minute / 12

    this.setProperties({ second, minute, hour })
  },

  timer: task(function*() {
    for (;;) {
      this._update()

      /* istanbul ignore else */
      if (testing) {
        return
      }

      /* istanbul ignore next */
      yield timeout(1000)
    }
  }).on('didInsertElement')
})

/**
 * @module timed
 * @submodule timed-components
 * @public
 */
import Component from '@ember/component'
import moment from 'moment'
import Ember from 'ember'
import { task, timeout } from 'ember-concurrency'

const { testing } = Ember

export default Component.extend({
  classNames: ['timed-clock'],

  hour: 0,
  minute: 0,
  second: 0,

  _update() {
    let now = moment()

    let second = now.seconds() * 6
    let minute = now.minutes() * 6 + second / 60
    let hour = (now.hours() % 12) / 12 * 360 + minute / 12

    this.setProperties({ second, minute, hour })
  },

  timer: task(function*() {
    for (;;) {
      this._update()

      /* istanbul ignore else */
      if (testing) {
        return
      }

      /* istanbul ignore next */
      yield timeout(1000)
    }
  }).on('didInsertElement')
})

Fix angle of clocks hour indicator


const path = require('path')

module.exports = {
  entry: ['babel-polyfill', './src/index.js'],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: '/dist/'
  },

  resolve: {
    modules: ['./src', 'node_modules'],
    extensions: ['.js', 'jsx']
  },

  module: {
    loaders: [{
      test: /\.js$/,
      exclude: [/node_modules/, /assets/],
      loader: 'babel-loader',
      include: __dirname,
      query: {
        presets: [ 'es2015', 'react' ],
        plugins: ['transform-class-properties', 'transform-object-rest-spread']
      }
    },
    {
      test: /\.(gif|png|jpe?g|svg)$/i,
      loaders: [
        'file-loader',
        {
          loader: 'image-webpack-loader',
          query: {
            progressive: true
            //optimizationLevel: 7,
            //interlaced: false,
          }
        }
      ]
    },
    { test: /\.css$/, loader: 'style-loader!css-loader' }]
  }
}

const path = require('path')

module.exports = {
  entry: ['babel-polyfill', './src/index.js'],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: '/dist/'
  },

  resolve: {
    modules: ['./src', 'node_modules'],
    extensions: ['.js', 'jsx']
  },

  module: {
    loaders: [{
      test: /\.js$/,
      exclude: [/node_modules/, /assets/],
      loader: 'babel-loader',
      include: __dirname,
      query: {
        presets: [ 'es2015', 'react' ],
        plugins: ['transform-class-properties', 'transform-object-rest-spread']
      }
    },
    {
      test: /\.(gif|png|jpe?g|svg)$/i,
      use: ['file-loader', {
          loader: 'image-webpack-loader',
          options: {
            mozjpeg: {
              progressive: true,
              quality: 65
            },
            optipng: {
              enabled: false,
            },
            pngquant: {
              quality: '65-90',
              speed: 4
            },
            gifsicle: {
              interlaced: false,
            },
          }
        },
      ],
      exclude: /node_modules/,
      include: __dirname,
    },
    { test: /\.css$/, loader: 'style-loader!css-loader' }]
  }
}

Fix deprecation image-loader warnings. Now each loader option must be passed to the correspondent optimizer.


module.exports = function(ctx) {
    var fs = ctx.requireCordovaModule('fs'),
        path = ctx.requireCordovaModule('path'),
        xml = ctx.requireCordovaModule('cordova-common').xmlHelpers;

    var manifestPath = path.join(ctx.opts.projectRoot, 'platforms/android/AndroidManifest.xml');
    var doc = xml.parseElementtreeSync(manifestPath);
    if (doc.getroot().tag !== 'manifest') {
        throw new Error(manifestPath + ' has incorrect root node name (expected "manifest")');
    }

    //adds the tools namespace to the root node
    // doc.getroot().attrib['xmlns:tools'] = 'http://schemas.android.com/tools';
    //add tools:replace in the application node
    doc.getroot().find('./application').attrib['android:name'] = 'android.support.multidex.MultiDexApplication';

    //write the manifest file
    fs.writeFileSync(manifestPath, doc.write({indent: 4}), 'utf-8');
};

module.exports = function(ctx) {
    var fs = ctx.requireCordovaModule('fs'),
        path = ctx.requireCordovaModule('path'),
        xml = ctx.requireCordovaModule('cordova-common').xmlHelpers;

    var manifestSubPaths = ['platforms/android/AndroidManifest.xml', 'platforms/android/app/src/main/AndroidManifest.xml'];
    var manifestPath = null;
    for (var i = 0, len = manifestSubPaths.length; i < len; i++) {
        var candidatePath = path.join(ctx.opts.projectRoot, manifestSubPaths[i]);
        if (fs.existsSync(candidatePath)) {
            manifestPath = candidatePath;
            break;
        }
    }
    if (manifestPath === null) {
        throw new Error('AndroidManifest.xml not found');
    }

    var doc = xml.parseElementtreeSync(manifestPath);
    if (doc.getroot().tag !== 'manifest') {
        throw new Error(manifestPath + ' has incorrect root node name (expected "manifest")');
    }

    //adds the tools namespace to the root node
    // doc.getroot().attrib['xmlns:tools'] = 'http://schemas.android.com/tools';
    //add tools:replace in the application node
    doc.getroot().find('./application').attrib['android:name'] = 'android.support.multidex.MultiDexApplication';

    //write the manifest file
    fs.writeFileSync(manifestPath, doc.write({indent: 4}), 'utf-8');
};

Support Cordova 7.0 file structure


exports.config = {
    framework: 'jasmine2',
    specs: ['spec.js'],
}

if (process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY) {
    exports.config.sauceUser = process.env.SAUCE_USERNAME
    exports.config.sauceKey = process.env.SAUCE_ACCESS_KEY
}

exports.config = {
    framework: 'jasmine2',
    specs: ['spec.js'],
}

if (process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY) {
    exports.config.sauceSeleniumAddress = 'localhost:4445/wd/hub'
    exports.config.sauceUser = process.env.SAUCE_USERNAME
    exports.config.sauceKey = process.env.SAUCE_ACCESS_KEY
}

Make use of Sauce Connect during Travis tests.


var fs = require('fs');
var jsss = require('./jsss');
var argv = process.argv;
var message = require('./message');
var pkg = fs.readFileSync(__dirname + '/../package.json');

if(argv[2] !== void 0) {
  switch (argv[2]) {
    case '-v':
      if (argv.length === 3) {
        jsss.version();
      }
      break;
    case '--version':
      if (argv.length === 3) {
        jsss.version();
      }
      break;
    case '-h':
      if (argv.length === 3) {
        jsss.help();
      }
      break;
    case '--help':
      if (argv.length === 3) {
        jsss.help();
      }
      break;
    default:
      if (argv.length === 4 || argv.length === 3) {
        var opt = {
          encoding: 'utf8'
        };

        var file = argv[2];
        var code = fs.readFileSync(file, opt);
        var css = jsss.parse(code);
        var out = argv[3] || false;

        if (!out) {
          process.stdout.write(css);
        } else {
          fs.writeFileSync(out, css);
        }
      } else {
        var message = 'Unrecognized command line argument: ';
        message += argv[2];
        message += ' ( see: \'jsss -h\' )';
        process.stdout.write(message);
        break;
      }
  }
} else {
  process.stdout.write(message());
  return;
}

var fs = require('fs');
var jsss = require('./jsss');
var argv = process.argv;
var message = require('./message');
var pkg = fs.readFileSync(__dirname + '/../package.json');

if(argv[2] !== void 0) {
  switch (argv[2]) {
    case '-v':
      if (argv.length === 3) {
        jsss.version();
      }
      break;
    case '--version':
      if (argv.length === 3) {
        jsss.version();
      }
      break;
    case '-h':
      if (argv.length === 3) {
        jsss.help();
      }
      break;
    case '--help':
      if (argv.length === 3) {
        jsss.help();
      }
      break;
    default:
      if (argv.length === 4 || argv.length === 3) {
        var file = argv[2];
        if (!fs.existsSync(file)) {
          process.stdout.write('No such file or directory "' + file + '"\n');
          return;
        }

        var opt = {
          encoding: 'utf8'
        };
        var code = fs.readFileSync(file, opt);
        var css = jsss.parse(code);
        var out = argv[3] || false;

        if (!out) {
          process.stdout.write(css);
        } else {
          fs.writeFileSync(out, css);
        }
      } else {
        var message = 'Unrecognized command line argument: ';
        message += argv[2];
        message += ' ( see: \'jsss -h\' )';
        process.stdout.write(message);
        break;
      }
  }
} else {
  process.stdout.write(message());
  return;
}

Add exception if file does not exists


require('@client-side-validations/client-side-validations')
require('@client-side-validations/simple-form/dist/simple-form.bootstrap4')

ClientSideValidations.callbacks.form.fail = ($form, eventData) => {
  const settings = $form[0].ClientSideValidations

  $('html, body').animate(
    { scrollTop: $form.find(`.${settings.html_settings.wrapper_error_class}`).offset().top - $('.main-navbar').outerHeight() - 10 }
    , 250)
}

$(document).on('change', '#itinerary_round_trip', function () {
  const status = $(this).prop('checked')
  $('select[id^="itinerary_return_date"]').prop('disabled', !status)
})

require('@client-side-validations/client-side-validations')
require('@client-side-validations/simple-form/dist/simple-form.bootstrap4')

ClientSideValidations.callbacks.form.fail = ($form, eventData) => {
  const settings = $form[0].ClientSideValidations.settings

  $('html, body').animate(
    { scrollTop: $form.find(`.${settings.html_settings.wrapper_error_class}`).offset().top - $('.main-navbar').outerHeight() - 10 }
    , 250)
}

$(document).on('change', '#itinerary_round_trip', function () {
  const status = $(this).prop('checked')
  $('select[id^="itinerary_return_date"]').prop('disabled', !status)
})

Fix scrolling to first JavaScript error