'use strict';

// DEV TIME CODE
if (FAKE_SERVER) {
    require('fake');
}
// DEV TIME CODE

require('./index.scss');

var $ = require('$jquery');
var state = require('./state');
var repository = require('./repository');
var sections = require('./sections/section');

//var details = args.newData.hud;
var setup = state.current();

// only load things when we have the data ready to go
repository.getData(function(details) {
	// generate the html needed for the sections
	var html = sections.render(details, setup);
	html = '

' + html + '

';

	// insert the html into the dom
	var holder = $(html).appendTo('body')

	// force the correct state from previous load
	state.setup(holder);

	// setup events that we need to listen to
	sections.postRender(holder);
});

'use strict';

// DEV TIME CODE
if (FAKE_SERVER) {
    require('fake');
}
// DEV TIME CODE

require('./index.scss');

var $ = require('$jquery');
var util = require('lib/util');
var state = require('./state');
var repository = require('./repository');
var sections = require('./sections/section');

//var details = args.newData.hud;
var setup = state.current();

// only load things when we have the data ready to go
repository.getData(function(details) {
	// generate the html needed for the sections
	var html = sections.render(details, setup);
	html = '

' + html + '

';

	// insert the html into the dom
	var holder = $(html).appendTo('body')

	// force the correct state from previous load
	state.setup(holder);

	// setup events that we need to listen to
	sections.postRender(holder);

	// TODO: need to find a better place for this
	$('.glimpse-icon').click(function() {
		window.open(util.resolveClientUrl(), 'GlimpseClient');
	});
});

Add click event to HUD glimpse logo to open the client


(function () {
    function getValidator (pageName,callName) {
        return function (params) {
            if (this.sys.validAdmin(params)
                && params.page === pageName
                && !params.cmd) {

                return true;
            }
            return false;
        }
    };
    exports.getValidator = getValidator;
})();

(function () {
    function getValidator (pageName,callName) {
        return function (params) {
            if (params.page === pageName
                && this.sys.validStaff(params)) {

                return true;
            }
            return false;
        }
    };
    exports.getValidator = getValidator;
})();

Allow anyone with a valid URL to get at the dynamic pending feed


define('helpers.events', function(require, module) {

  var listeners = {};
  module.exports.addListener = function(name) {
    return function(f) {
      if(!listeners[name]) {
        listeners[name] = [f];
      } else {
        listeners[name].push(f);
      }
    };
  };

  module.exports.fire = function(name) {
    return function() {
      for (var i = 0; i < listeners[name].length; i++) {
        listeners[name][i]();
      }
    };
  };
});

define('helpers.events', function(require, module) {

  var listeners = {};
  module.exports.addListener = function(name) {
    return function(f) {
      if(!listeners[name]) {
        listeners[name] = [f];
      } else {
        listeners[name].push(f);
      }
    };
  };

  module.exports.fire = function(name) {
    return function() {
      var len = listeners[name] ? listeners[name].length : 0;
      for (var i = 0; i < len; i++) {
        listeners[name][i]();
      }
    };
  };
});

Fix firing a chrome event when there are no listeners.


class LoggingEvent {
  constructor(categoryName, level, message, exception, logger) {
    this.categoryName = categoryName;
    this.level = level;
    this.message = message;
    this.exception = exception;
    this.logger = logger;

    this.startTime = new Date();
  }
}

export default LoggingEvent;

class LoggingEvent {
  constructor(categoryName, level, message, exception) {
    this.categoryName = categoryName;
    this.level = level;
    this.message = message;
    this.exception = exception;

    this.startTime = new Date();
  }
}

export default LoggingEvent;

Remove logger reference from LoggingEvent


import React from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import ReactGA from 'react-ga';

class GoogleAnalytics extends React.Component {
  componentDidMount() {
    ReactGA.initialize('UA-000000-01', {
      debug: true
    });
  }

  componentDidUpdate(prevProps) {
    const thisUrl = this.props.location.pathname + this.props.location.search;
    const prevUrl = prevProps.location.pathname + prevProps.location.search;
    if (thisUrl !== prevUrl) {
      ReactGA.set({ page: thisUrl });
      ReactGA.pageview(thisUrl);
    }
  }

  render() {
    return this.props.children;
  }
}

GoogleAnalytics.propTypes = {
  location: PropTypes.object.isRequired,
  children: PropTypes.object.isRequired
};

export default withRouter(GoogleAnalytics);

import React from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import ReactGA from 'react-ga';

class GoogleAnalytics extends React.Component {
  componentDidMount() {
    ReactGA.initialize('UA-65302999-1', {
      debug: true
    });
  }

  componentDidUpdate(prevProps) {
    const thisUrl = this.props.location.pathname + this.props.location.search;
    const prevUrl = prevProps.location.pathname + prevProps.location.search;
    if (thisUrl !== prevUrl) {
      ReactGA.set({ page: thisUrl });
      ReactGA.pageview(thisUrl);
    }
  }

  render() {
    return this.props.children;
  }
}

GoogleAnalytics.propTypes = {
  location: PropTypes.object.isRequired,
  children: PropTypes.object.isRequired
};

export default withRouter(GoogleAnalytics);

Add the google analytics key


import axios from 'axios'
import { API } from '../constants'

export const LOGIN = 'LOGIN'
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
export const LOGIN_FAILURE = 'LOGIN_FAILURE'

export const login = creds => async dispatch => {
	dispatch({ type: LOGIN })
	try {
		const response = await axios.post(`http://localhost:8000/login`, creds)
		dispatch({ type: LOGIN_SUCCESS, token: response.data['token'] })
	} catch (err) {
		const response = err.response
		const error = response ? response.data : err.message
		dispatch({ type: LOGIN_FAILURE, error })
	}
}

import axios from 'axios'
import { API } from '../constants'

export const LOGIN = 'LOGIN'
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
export const LOGIN_FAILURE = 'LOGIN_FAILURE'

export const login = creds => async dispatch => {
	dispatch({ type: LOGIN })
	try {
		const response = await axios.post(`${API}/login`, creds)
		dispatch({ type: LOGIN_SUCCESS, token: response.data['token'] })
	} catch (err) {
		const response = err.response
		const error = response ? response.data : err.message
		dispatch({ type: LOGIN_FAILURE, error })
	}
}

Change hardcoded url to constant


const directive = {
  instances: []
}

directive.onEvent = function (event) {
  directive.instances.forEach(({ el, fn }) => {
    if (event.target !== el && !el.contains(event.target)) {
      fn && fn(event)
    }
  })
}

directive.bind = function (el) {
  directive.instances.push({ el, fn: null })
  if (directive.instances.length === 1) {
    document.addEventListener('click', directive.onEvent)
  }
}

directive.update = function (el, binding) {
  if (typeof binding.value !== 'function') {
    throw new Error('Argument must be a function')
  }
  const instance = directive.instances.find(i => i.el === el)
  instance.fn = binding.value
}

directive.unbind = function (el) {
  const instance = directive.instances.find(i => i.el === el)
  const instanceIndex = directive.instances.indexOf(instance)
  directive.instances.splice(instanceIndex, 1)
  if (directive.instances.length === 0) {
    document.removeEventListener('click', directive.onEvent)
  }
}

export default directive

const userAgent = navigator.userAgent.toLowerCase()
const event = userAgent.match(/(iphone|ipod|ipad)/) ? "touchstart" : "click"

const directive = {
  instances: []
}

directive.onEvent = function (event) {
  directive.instances.forEach(({ el, fn }) => {
    if (event.target !== el && !el.contains(event.target)) {
      fn && fn(event)
    }
  })
}

directive.bind = function (el) {
  directive.instances.push({ el, fn: null })
  if (directive.instances.length === 1) {
    document.addEventListener(event, directive.onEvent)
  }
}

directive.update = function (el, binding) {
  if (typeof binding.value !== 'function') {
    throw new Error('Argument must be a function')
  }
  const instance = directive.instances.find(i => i.el === el)
  instance.fn = binding.value
}

directive.unbind = function (el) {
  const instance = directive.instances.find(i => i.el === el)
  const instanceIndex = directive.instances.indexOf(instance)
  directive.instances.splice(instanceIndex, 1)
  if (directive.instances.length === 0) {
    document.removeEventListener(event, directive.onEvent)
  }
}

export default directive

Fix iPhone/iPad not triggering click event


define(['knockout', 'jquery', './model',
        'knockout.punches',
        'template!./template/index.html!<%= model.name %>-main',
    ],
    function(ko, $, ViewModel) {

        ko.punches.enableAll();

        ko.bindingHandlers.<%= model.name %> = {
            init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

                var value = ko.utils.unwrapObservable(valueAccessor());
                element.model = new ViewModel();
                ko.renderTemplate("<%= model.name %>-main", element.model, null, element, "replaceChildren");

            },
            update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

                element.value = ko.utils.unwrapObservable(valueAccessor());
            }
        };
    });

define(['knockout', 'jquery', 'lodash', './model',
        'knockout.punches',
        'template!./template/index.html!<%= model.name %>-main',
    ],
    function(ko, $, _, ViewModel) {

        ko.punches.enableAll();

        ko.bindingHandlers.<%= model.name %> = {
            init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

                var value = ko.utils.unwrapObservable(valueAccessor());
                element.model = new ViewModel();
                element.model.instanceName = allBindings.get('id') || _.uniqueId("<%= model.name %>--");
                ko.renderTemplate("<%= model.name %>-main", element.model, null, element, "replaceChildren");

            },
            update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

                element.value = ko.utils.unwrapObservable(valueAccessor());
            }
        };
    });

Add instance name logic for unique ids in binding handlers


var cadence = require('cadence')
var interrupt = require('interrupt').createInterrupter('vizsla')
var Parser = require('./parser')
var util = require('util')
var coalesce = require('extant')
var stream = require('stream')

function Raiseify (when) {
    Parser.call(this, coalesce(when, [ -2 ]), [])
}
util.inherits(Raiseify, Parser)

Raiseify.prototype._parse = function (body, response, callback) {
    if (body instanceof stream.Readable) {
        body.resume()
    }
    callback(interrupt('error', response))
}

module.exports = function () {
    return new Raiseify
}

var cadence = require('cadence')
var interrupt = require('interrupt').createInterrupter('vizsla')
var Selector = require('./select')
var coalesce = require('extant')
var stream = require('stream')

function Raiseify (when) {
    this._select = Selector(coalesce(when, [ -2 ]))
}

Raiseify.prototype.descend = cadence(function (async, descent) {
    async(function () {
        descent.descend(async())
    }, function (body, response) {
        if (this._select.call(null, response)) {
            if (body instanceof stream.Readable) {
                body.resume()
            }
            throw interrupt('error', response)
        }
    })
})

module.exports = function () {
    return new Raiseify
}

Raise even when body is `null`.


angular.module('app').constant('UAA_ENDPOINT', 'https://uaa.run.pivotal.io');
angular.module('app').constant('API_ENDPOINT', 'https://api.run.pivotal.io');

//angular.module('app').constant('UAA_ENDPOINT', 'http://uaa.cf.cclab.io');
//angular.module('app').constant('API_ENDPOINT', 'http://api.cf.cclab.io');

//angular.module('app').constant('UAA_ENDPOINT', 'https://uaa.run.pivotal.io');
//angular.module('app').constant('API_ENDPOINT', 'https://api.run.pivotal.io');

angular.module('app').constant('UAA_ENDPOINT', 'http://uaa.cf.cclab.io');
angular.module('app').constant('API_ENDPOINT', 'http://api.cf.cclab.io');

Rename login folder to logIn.


/**
 * Created by XiYe on 12/23/2016.
 */

const mongoose = require('mongoose')

const ScheduleSchema = new mongoose.Schema({
  place: {
    type: String
  },
  type: {
    type: String
  },
  startTime: {
    type: String
  },
  dueTime: {
    type: String
  },
  date: {
    type: String
  },
  content: {
    type: String
  },
  description: {
    type: String
  }
})

const ScheduleSchema = mongoose.model('ScheduleSchema', ScheduleSchema)

module.exports.ScheduleSchema = ScheduleSchema
module.exports.ScheduleSchema = ScheduleSchema

/**
 * Created by XiYe on 12/23/2016.
 */

const mongoose = require('mongoose')

const ScheduleSchema = new mongoose.Schema({
  place: {
    type: String
  },
  type: {
    type: String
  },
  startTime: {
    type: String
  },
  dueTime: {
    type: String
  },
  date: {
    type: String
  },
  content: {
    type: String
  },
  description: {
    type: String
  }
}, {
  toObject: {
    transform: (doc, ret) => {
      delete ret._id
    }
  }
})

const ScheduleSchema = mongoose.model('ScheduleSchema', ScheduleSchema)

module.exports.ScheduleSchema = ScheduleSchema
module.exports.ScheduleSchema = ScheduleSchema

Add to Object function for security


import { getRandomPhoto } from '../api';

/**
 * Fetch a random photo from the server
 */

const fetchRandomPhoto = () => {
  const sendRequest = collections => {
    getRandomPhoto(collections)
      .then(data => {
        const nextImage = Object.assign(
          {
            timestamp: Date.now(),
          },
          data
        );

        chrome.storage.local.set({ nextImage });

        chrome.storage.local.get(['history', 'photoFrequency'], result => {
          const history = result.history || [];

          if (history.length >= 10) {
            history.pop();
          }

          history.unshift(nextImage);
          chrome.storage.local.set({ history });

          const { photoFrequency } = result;

          if (photoFrequency === 'every15minutes') {
            chrome.alarms.create('loadphoto', {
              periodInMinutes: 15,
            });
          }

          if (photoFrequency === 'everyhour') {
            chrome.alarms.create('loadphoto', {
              periodInMinutes: 60,
            });
          }

          if (photoFrequency === 'everyday') {
            chrome.alarms.create('loadphoto', {
              periodInMinutes: 1440,
            });
          }
        });
      })
      .catch(console.error);
  };

  chrome.storage.sync.get('imageSource', result => {
    let collections = '998309';

    if (result.imageSource === 'custom') {
      return chrome.storage.sync.get('collections', r => {
        collections = r.collections;
        sendRequest(collections);
      });
    }

    return sendRequest(collections);
  });
};

export default fetchRandomPhoto;

import { getRandomPhoto } from '../api';

/**
 * Fetch a random photo from the server
 */

const fetchRandomPhoto = () => {
  const sendRequest = collections => {
    getRandomPhoto(collections)
      .then(data => {
        const nextImage = Object.assign(
          {
            timestamp: Date.now(),
          },
          data
        );

        chrome.storage.local.set({ nextImage });

        chrome.storage.local.get(['history'], result => {
          const history = result.history || [];

          if (history.length >= 10) {
            history.pop();
          }

          history.unshift(nextImage);
          chrome.storage.local.set({ history });
        });
      })
      .catch(console.error);
  };

  chrome.storage.sync.get('imageSource', result => {
    let collections = '998309';

    if (result.imageSource === 'custom') {
      return chrome.storage.sync.get('collections', r => {
        collections = r.collections;
        sendRequest(collections);
      });
    }

    return sendRequest(collections);
  });
};

export default fetchRandomPhoto;

Delete redundant alarms as they're already set


"use strict";

self.onmessage = function(event) {
	var
	    workload = event.data
	  , a = 0x08a90db3, b = 0xabd209a0, c = 0x29019b32, d = 0x01ab3291
	;

	for (var i = 0; i < workload; i++) {
		a = (b ^ a) >> 1;
		b = (c ^ b) << 1;
		c = (d ^ c) >> 1;
		d = (a ^ d) << 1;
	}

	postMessage(null);
};

"use strict";

self.onmessage = function(event) {
	var
	    workload = event.data
	  , a = 0x08a90db3, b = 0xabd209a0, c = 0x29019b32, d = 0x01ab3291
	;

	for (var i = 0; i < workload; i++) {
		a = (b ^ a) >> 1;
		b = (c ^ b) << 1;
		c = (d ^ c) >> 1;
		d = (a ^ d) << 1;
	}

	postMessage(a);
};

Make sure the browser thinks it's doing actual work


const path = require('path')

module.exports = {
  extends: [
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier',
    'prettier/@typescript-eslint',
  ],
  overrides: [
    {
      files: ['*.js'],
      rules: {
        '@typescript-eslint/no-var-requires': 'off',
      },
    },
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    project: path.join(__dirname, 'tsconfig.eslint.json'),
    sourceType: 'module',
  },
  plugins: ['react', 'react-hooks', '@typescript-eslint', 'simple-import-sort'],
  root: true,
  rules: {
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    'react-hooks/exhaustive-deps': 'warn',
    'react-hooks/rules-of-hooks': 'error',
    'react/jsx-sort-props': 'error',
    'react/jsx-uses-react': 'off',
    'react/react-in-jsx-scope': 'off',
    'simple-import-sort/exports': 'error',
    'simple-import-sort/imports': 'error',
    'sort-imports': 'off',
    'sort-keys': 'error',
  },
  settings: {
    react: {
      version: 'detect',
    },
  },
}

const path = require('path')

module.exports = {
  extends: [
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier',
  ],
  overrides: [
    {
      files: ['*.js'],
      rules: {
        '@typescript-eslint/no-var-requires': 'off',
      },
    },
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    project: path.join(__dirname, 'tsconfig.eslint.json'),
    sourceType: 'module',
  },
  plugins: ['react', 'react-hooks', '@typescript-eslint', 'simple-import-sort'],
  root: true,
  rules: {
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    'react-hooks/exhaustive-deps': 'warn',
    'react-hooks/rules-of-hooks': 'error',
    'react/jsx-sort-props': 'error',
    'react/jsx-uses-react': 'off',
    'react/react-in-jsx-scope': 'off',
    'simple-import-sort/exports': 'error',
    'simple-import-sort/imports': 'error',
    'sort-imports': 'off',
    'sort-keys': 'error',
  },
  settings: {
    react: {
      version: 'detect',
    },
  },
}

Fix eslint config for v8


const WebSocket = require('ws');
const ConnectionPool = require('./connection-pool.js');
const port = 3000;

class Server {
  constructor() {
    this.conPool = new ConnectionPool();
  }

  start() {
    const wss = new WebSocket.Server({ port: port });
    log('Listening on port %d...', port);

    try {
      wss.on('connection', ws => {
        try {
          log('New Connection');
          this.conPool.newVisitor(ws);
        } catch(e) {
          log('ERROR handling new Visitor:\n',e);
        }
      });
    } catch(e) {
      log('ERROR listening for connections:\n', e);
    }

    wss.on('error', e => log('Wss error:\n', e));
  }
}

module.exports = { Server };

const WebSocket = require('ws');
const ConnectionPool = require('./connection-pool.js');
const port = 3000;

class Server {
  constructor() {
    this.conPool = new ConnectionPool();
  }

  start() {
    const wss = new WebSocket.Server({ port: port });
    log('Listening on port %d...', port);

    try {
      wss.on('connection', ws => {
        ws.on('error', e => log('ws error:\n', e));
        try {
          log('New Connection');
          this.conPool.newVisitor(ws);
        } catch(e) {
          log('ERROR handling new Visitor:\n',e);
        }
      });
    } catch(e) {
      log('ERROR listening for connections:\n', e);
    }

    wss.on('error', e => log('Wss error:\n', e));
  }
}

module.exports = { Server };

Add error handling on individual websockets too


import React from 'react';

const Footer = (props) => { // eslint-disable-line no-unused-vars
  return (

        A FreeCodeCamp initiative.

  );
};

export default Footer;

Add FCC icon in footer


define([
    'utils/events'
], function (Events) {
    /**
     * Rockabox JSHook class, it will setup custom hooks set in the Placement js
     * for Load, Init, View initial, Engagement and Click.
     *
     * @param {object} advert      The Advert class
     * @param {object} customHooks Custom hooks set within Placement js
     */
    function JSHooks (advert, customHooks) {
        var $this = this,
            events = new Events(),
            hooks = [
                'load',
                'init',
                'viewInitial',
                'engagement',
                'click'
            ];

        /**
         * Initializes the js hooks
         */
        function init () {
            for (var hook in hooks) {
                var hookName = hooks[hook],
                    hookEvent = 'rb.' + hookName;

                events.addListener(advert.getCreative(), hookEvent, customHooks[hookName]);
            }
        }

        init();
    }

    return JSHooks;
});

define([
    'utils/events'
], function (Events) {
    var events = new Events();
    /**
     * Rockabox JSHook class, it will setup custom hooks set in the Placement js
     * for Load, Init, View initial, Engagement and Click.
     *
     * @param {object} advert      The Advert class
     * @param {object} customHooks Custom hooks set within Placement js
     */
    function JSHooks (advert, customHooks) {
        var $this = this,
            hooks = [
                'load',
                'init',
                'viewInitial',
                'engagement',
                'click'
            ];

        /**
         * Initializes the js hooks
         */
        function init () {
            for (var hook in hooks) {
                var hookName = hooks[hook],
                    hookEvent = 'rb.' + hookName;

                events.addListener(advert.getCreative(), hookEvent, customHooks[hookName]);
            }
        }

        init();
    }

    return {
        init: JSHooks,
        events: events
    };
});

Split out Load Creative & JSHooks test fix


import { h, render } from 'preact';

import { HTMLElement, sym } from './util';
import { withProps } from './with-props';
import { withRender } from './with-render';
import { withUnique } from './with-unique';

const _preactDom = sym('_preactDom');

export const withComponent = (Base = HTMLElement) => class extends withUnique(withRender(withProps(Base))) {
  rendererCallback (shadowRoot, renderCallback) {
    const dom = render(renderCallback(), shadowRoot, this[_preactDom]);
    if (!this[_preactDom]) {
      this[_preactDom] = dom;
    }
  }
};

export const Component = withComponent();
export { h };

import { h, render } from 'preact';

import { HTMLElement, sym } from './util';
import { withProps } from './with-props';
import { withRender } from './with-render';
import { withUnique } from './with-unique';

const _preactDom = sym('_preactDom');

export const withComponent = (Base = HTMLElement) => class extends withUnique(withRender(withProps(Base))) {
  rendererCallback (shadowRoot, renderCallback) {
    this[_preactDom] = render(renderCallback(), shadowRoot, this[_preactDom]);
  }
};

export const Component = withComponent();
export { h };

Save _preactDom on every rendererCallback


'use strict'

const bluebird = require('bluebird')
const path = require('path')
const writeFile = bluebird.promisify(require('fs').writeFile)

module.exports = function () {
  this.When(/^I add a new spec file$/, function () {
    const newSpecFile = path.join(this.testDir, 'new_test.js')
    const specText = "describe('A suite', function() {\n" +
    "it('contains spec 2 with an expectation', function() {\n" +
    "expect(dependency()).toBe('foobar')\n" +
    '})\n' +
    '})'
    return writeFile(newSpecFile, specText)
  })
}

'use strict'

const bluebird = require('bluebird')
const path = require('path')
const writeFile = bluebird.promisify(require('fs').writeFile)

module.exports = function () {
  this.When(/^I add a new spec file$/, function () {
    const newSpecFile = path.join(this.testDir, 'new_test.js')
    const specText = "describe('A suite', function() {\n" +
    "it('contains spec 2 with an expectation', function() {\n" +
    "expect('foobar').toBe('foobar')\n" +
    '})\n' +
    '})'
    return writeFile(newSpecFile, specText)
  })
}

Fix missing dependency on spec, scenario passes


export const createInvitation = ({ Email, Type } = {}) => ({
    method: 'post',
    url: 'invites',
    data: { Email, Type }
});

export const checkInvitation = (data) => ({
    method: 'post',
    url: 'invites/check',
    data
});

Add route to check invite


// This loader replaces each require(*.coffee) by require(./noco-loader.js!*.coffee)
// and compile the result to JS.
// Its purpose is to avoid adding any code in the webpack.config.js of the parent project.

var coffee = require('coffee-script');
var path = __filename.replace(/\\/g,'/');
var exp = RegExp(/require([\(,',",\s]*)(.*\.coffee[\),',",\s]*)/g)
var has_coffee_config = false;

module.exports = function(source, map) {
    var matches = source.match(exp);
    if(matches != null){
        for (i = 0; i < matches.length; i++) {
            var match = matches[i];
            if(match.indexOf('!') != -1){continue};
            var replace = match.replace(exp,'require$1' + path + '!$2');
            source = source.replace(match,replace);
        }
    }

    if(/(^|\n)var /.test(source))
        has_coffee_config = true;

    if(!has_coffee_config)
        source = coffee.compile(source);
    this.callback(null, source, map);
};

// This loader replaces each require(*.coffee) by require(./noco-loader.js!*.coffee)
// and compile the result to JS.
// Its purpose is to avoid adding any code in the webpack.config.js of the parent project.

var coffee = require('coffee-script');
var path = __filename.replace(/\\/g,'/');
var exp = RegExp(/require([\(,',",\s]*)(.*\.coffee[\),',",\s]*)/g)
var has_coffee_config = false;

module.exports = function(source, map) {
    var matches = source.match(exp);
    if(matches != null){
        for (i = 0; i < matches.length; i++) {
            var match = matches[i];
            if(match.indexOf('!') != -1){continue};
            var replace = match.replace(exp,'require$1' + path + '!$2');
            source = source.replace(match,replace);
        }
    }

    if(/(^|\n)var /.test(source))
        has_coffee_config = true;

    if(!has_coffee_config)
        source = coffee.compile(source);
    this.cacheable(true)
    this.callback(null, source, map);
};

Add cacheable flag to webpack loader.


/*jslint indent: 2 */
/*global $: false, document: false, togglbutton: false*/

'use strict';

// List items
togglbutton.render('.list li:not(.toggl)', {observe: true}, function (elem) {
  var link,
    taskElement = $('.task-title', elem),
    description = $('a', taskElement).textContent.trim(),
    projectName = function () {
      var label = $('span.highlight', taskElement);
      if (!!label) {
        return label.textContent;
      }
      return "";
    };

  link = togglbutton.createTimerLink({
    className: 'capsule',
    description: description,
    projectName: projectName,
    buttonType: 'minimal'
  });

  taskElement.appendChild(link);
});

// List items in new UI
togglbutton.render('.task-item:not(.toggl)', {observe: true}, function (elem) {
  var link,
    taskElement = $('.task-item-title', elem),
    description = function () {
      var desc = $('.task-item-title-text', elem);
      if (!!desc) {
        return desc.textContent.trim();
      }
      return "";
    },
    projectName = function () {
      var label = $('span.task-item-category', elem);
      if (!!label) {
        return label.textContent;
      }
      return "";
    };

  link = togglbutton.createTimerLink({
    className: 'capsule',
    description: description,
    projectName: projectName,
    buttonType: 'minimal'
  });

  taskElement.appendChild(link);
});

/*jslint indent: 2 */
/*global $: false, document: false, togglbutton: false*/

'use strict';

// List items
togglbutton.render('.list li:not(.toggl)', {observe: true}, function (elem) {
  var link,
    taskElement = $('.task-title', elem),
    description = $('a', taskElement).textContent.trim(),
    projectName = function () {
      var label = $('span.highlight', taskElement);
      if (!!label) {
        return label.textContent;
      }
      return "";
    };

  link = togglbutton.createTimerLink({
    className: 'capsule',
    description: description,
    projectName: projectName,
    buttonType: 'minimal'
  });

  taskElement.appendChild(link);
});

// List items in new UI
togglbutton.render('.general-task-item:not(.toggl)', {observe: true}, function (elem) {
  var link,
    taskElement = $('.general-task-item-title', elem),
    description = function () {
      var desc = $('.general-task-item-title-text', elem);
      if (!!desc) {
        return desc.textContent.trim();
      }
      return "";
    },
    projectName = function () {
      var label = $('.general-task-item-category', elem);
      if (!!label) {
        return label.textContent;
      }
      return "";
    };

  link = togglbutton.createTimerLink({
    className: 'capsule',
    description: description,
    projectName: projectName,
    buttonType: 'minimal'
  });

  taskElement.appendChild(link);
});

Update selectors in Capsule CRM integration


var path 		= require('path');
var rootPath 	= path.normalize(__dirname + '/../../');

module.exports 	= {
    local: {
        baseUrl: 'http://localhost:3051',
        db: 'mongodb://localhost/rp_local',
        rootPath: rootPath,
        port: process.env.PORT || 3051
    },
    staging : {
        baseUrl: 'http://dev.resourceprojects.org',
        //db: '@aws-us-east-1-portal.14.dblayer.com:10669/rp_dev?ssl=true',
        db: '@aws-us-east-1-portal.14.dblayer.com:10669,aws-us-east-1-portal.13.dblayer.com:10499/rp_dev?ssl=true',
        rootPath: rootPath,
        port: process.env.PORT || 80
    }
};

var path 		= require('path');
var rootPath 	= path.normalize(__dirname + '/../../');

module.exports 	= {
    local: {
        baseUrl: 'http://localhost:3030',
        db: 'mongodb://localhost/rp_local',
        rootPath: rootPath,
        port: process.env.PORT || 3030
    },
    staging : {
        baseUrl: 'http://dev.resourceprojects.org',
        //db: '@aws-us-east-1-portal.14.dblayer.com:10669/rp_dev?ssl=true',
        db: '@aws-us-east-1-portal.14.dblayer.com:10669,aws-us-east-1-portal.13.dblayer.com:10499/rp_dev?ssl=true',
        rootPath: rootPath,
        port: process.env.PORT || 80
    }
};

Add to download links ng-csv


'use strict';

const main = require('../app/controllers/main');
const api = require('../app/controllers/api');
const auth = require('../app/controllers/auth');

/**
 * Expose routes
 */

module.exports = function applyRoutes(app) {
  app.get('/', main.index);
  app.post('/auth/token', auth.token);
  app.get('/:type(orders|accounts|portions|vendors)', api);
  app.get('/:type(orders|accounts|portions|vendors)/:id', api);
  app.post('/:type(orders|accounts|portions|vendors)', api);
};

'use strict';

const passport = require('passport');
const main = require('../app/controllers/main');
const api = require('../app/controllers/api');
const auth = require('../app/controllers/auth');

/**
 * Expose routes
 */

module.exports = function applyRoutes(app) {
  app.get('/', main.index);
  app.post('/auth/token', auth.token);
  app.get('/me', passport.authenticate('jwt'), function(req, res) {
    res.send(req.user);
  });
  app.get('/:type(orders|accounts|portions|vendors)', api);
  app.get('/:type(orders|accounts|portions|vendors)/:id', api);
  app.post('/:type(orders|accounts|portions|vendors)', api);
};

Add API endpoint to get current user info


import stripSharedIndent from '../../src/utils/stripSharedIndent.js';
import { convert } from '../../dist/decaffeinate.cjs.js';
import { strictEqual } from 'assert';

export default function check(source, expected) {
  let converted = convert(stripSharedIndent(source));
  strictEqual(converted.code, stripSharedIndent(expected));
}

import PatchError from '../../src/utils/PatchError.js';
import stripSharedIndent from '../../src/utils/stripSharedIndent.js';
import { convert } from '../../dist/decaffeinate.cjs.js';
import { strictEqual } from 'assert';

export default function check(source, expected) {
  try {
    let converted = convert(stripSharedIndent(source));
    strictEqual(converted.code, stripSharedIndent(expected));
  } catch (err) {
    if (PatchError.isA(err)) {
      console.error(PatchError.prettyPrint(err));
    }
    throw err;
  }
}

Print more information about PatchErrors in tests.


var AWS = require('aws-sdk');

// Hardcoding keys on production is bad for your general health
AWS.config.update({accessKeyId: 'YOURACCESSKEYHERE', secretAccessKey: 'YOURSECRET', region: 'YOURREGION'});
var sns = new AWS.SNS({apiVersion: '2010-03-31'});

var lambdapayload = JSON.stringify({
    message: 'Hello World',
    custom: {
        senderid: 'testsenderid',
        recipientid: 'testrecipient',
        recipientname: 'Test Recipientname',
        messageid: 'testmsgid',
        devicetoken: 'token',
        deviceplatform: 'ios'
    }
});
var messagepayload = JSON.stringify({
    default: 'This is a default message',
    lambda: lambdapayload
});
var params = {
    Message: messagepayload,
    MessageStructure: 'json',
    TopicArn: 'THETOPICTOPUBLISHTO'
};
sns.publish(params, function(err,data) {
    console.log(err);
    console.log(data);
});

var AWS = require('aws-sdk');

// Hardcoding keys on production is bad for your general health
AWS.config.update({accessKeyId: 'YOURACCESSKEYHERE', secretAccessKey: 'YOURSECRET', region: 'YOURREGION'});
var sns = new AWS.SNS({apiVersion: '2010-03-31'});

var lambdapayload = JSON.stringify({
    message: 'Hello World',
    custom: {
        senderid: 'testsenderid',
        localizationkey: "LOCALIZATION_KEY_IN_STRINGS_FILE",
        sound: "default",
        recipientid: 'testrecipient',
        recipientname: 'Test Recipientname',
        messageid: 'testmsgid',
        devicetoken: 'token',
        deviceplatform: 'ios'
    }
});
var messagepayload = JSON.stringify({
    default: 'This is a default message',
    lambda: lambdapayload
});
var params = {
    Message: messagepayload,
    MessageStructure: 'json',
    TopicArn: 'THETOPICTOPUBLISHTO'
};
sns.publish(params, function(err,data) {
    console.log(err);
    console.log(data);
});

Add localization key and sound


import { Class } from '../lib/objects';
import { isQueryExpression } from './expression';
import QueryContext from './context';

export default Class.extend({
  target: null,

  operators: null,

  init(target, operators) {
    this.target = target;
    this.operators = operators;
  },

  evaluate(expression, _context) {
    if (isQueryExpression(expression)) {
      let operator = this.operators[expression.op];
      if (!operator) {
        throw new Error('Unable to find operator: ' + expression.op);
      }
      let context = _context || {};
      return operator.apply(this, [context].concat(expression.args));
    } else {
      return expression;
    }
  }
});

import { isQueryExpression } from './expression';

export default class QueryEvaluator {
  constructor(target, operators) {
    this.target = target;
    this.operators = operators;
  }

  evaluate(expression, _context) {
    if (isQueryExpression(expression)) {
      let operator = this.operators[expression.op];
      if (!operator) {
        throw new Error('Unable to find operator: ' + expression.op);
      }
      let context = _context || {};
      return operator.apply(this, [context].concat(expression.args));
    } else {
      return expression;
    }
  }
}

Convert QueryEvaluator to ES class.


Add missing Tetris Attack combo scores


'use strict';

var path = require('path');
var BinWrapper = require('bin-wrapper');
var pkg = require('../package.json');
var url = 'https://raw.github.com/imagemin/mozjpeg-bin/' + pkg.version + '/vendor/';

module.exports = new BinWrapper()
	.src(url + 'osx/cjpeg', 'darwin')
	.src(url + 'linux/cjpeg', 'linux')
	.src(url + 'win/cjpeg.exe', 'win32')
	.dest(path.join(__dirname, '../vendor'))
	.use(process.platform === 'win32' ? 'cjpeg.exe' : 'cjpeg');

'use strict';

var path = require('path');
var BinWrapper = require('bin-wrapper');
var pkg = require('../package.json');
var url = 'https://raw.githubusercontent.com/imagemin/mozjpeg-bin/v' + pkg.version + '/vendor/';

module.exports = new BinWrapper()
	.src(url + 'osx/cjpeg', 'darwin')
	.src(url + 'linux/cjpeg', 'linux')
	.src(url + 'win/cjpeg.exe', 'win32')
	.dest(path.join(__dirname, '../vendor'))
	.use(process.platform === 'win32' ? 'cjpeg.exe' : 'cjpeg');

Change version tag to include v


var mongoose = require('mongoose');
var mubsub = require('mubsub');

var monq = module.exports = {};

monq.Job = require('./job');
monq.Queue = require('./queue');
monq.Worker = require('./worker');

monq.pubsub = mubsub.channel('events');

monq.worker = function(options) {
    options.pubsub || (options.pubsub = monq.pubsub);
    return new monq.Worker(options);
};

monq.queue = function(name) {
    return new monq.Queue(name);
};

monq.subscribe = function(id, callback) {
    return monq.pubsub.subscribe({ 'job._id': id }, callback);
};

monq.connect = function(db) {
    mongoose.connect(db);
    mubsub.connect(db);
    return monq;
};

monq.disconnect = function() {
    mongoose.disconnect();
    mubsub.disconnect();
};

var mongoose = require('mongoose');
var mubsub = require('mubsub');

var monq = module.exports = {};

monq.Job = require('./job');
monq.Queue = require('./queue');
monq.Worker = require('./worker');

monq.pubsub = mubsub.channel('events');

monq.worker = function(options) {
    options || (options = {});
    options.pubsub || (options.pubsub = monq.pubsub);
    return new monq.Worker(options);
};

monq.queue = function(name) {
    return new monq.Queue(name);
};

monq.subscribe = function(id, callback) {
    return monq.pubsub.subscribe({ 'job._id': id }, callback);
};

monq.connect = function(db) {
    mongoose.connect(db);
    mubsub.connect(db);
    return monq;
};

monq.disconnect = function() {
    mongoose.disconnect();
    mubsub.disconnect();
};

Handle worker with no options


'use strict';

const events = require('events');
const utils = require('util');
const base = require('./base.js');
const protos = require('./proto.js');
const statics = require('./static.js');
const validators = require('./validator.js');

exports.createModel = createModel;

function createModel (name, properties) {
  if (typeof name !== 'string') throw new TypeError('Model name required');

  function Model(attrs) {
    if (!(this instanceof Model)) {
      return new Model(attrs);
    }

    events.EventEmitter.call(this);

    if (properties && attrs) {
      let self = this;
      Object.keys(attrs).forEach(function (key) {
        self[key] = attrs[key];
      });
      self.model.emit('construct', this, attrs);
    }
  }
  inherits(Model, name, properties);

  return Model;
}

function inherits(Model, name, properties) {
  Model.modelName = name;
  utils.inherits(Model, events.EventEmitter);
  base.setProperties(Model);
  statics.setProperties(Model);
  if (properties) protos.setProperties(Model, properties);
}

'use strict';

const events = require('events');
const utils = require('util');
const base = require('./base.js');
const protos = require('./proto.js');
const statics = require('./static.js');
const validators = require('./validator.js');

exports.createModel = createModel;

function createModel (name, properties) {
  if (typeof name !== 'string') throw new TypeError('Model name required');

  function Model(attrs) {
    if (!(this instanceof Model)) {
      return new Model(attrs);
    }

    events.EventEmitter.call(this);

    if (attrs) {
      let self = this;
      Object.keys(attrs).forEach(function (key) {
        self[key] = attrs[key];
      });
      self.model.emit('construct', this, attrs);
    }
  }
  inherits(Model, name, properties);

  return Model;
}

function inherits(Model, name, properties) {
  Model.modelName = name;
  utils.inherits(Model, events.EventEmitter);
  base.setProperties(Model);
  statics.setProperties(Model);
  if (properties) protos.setProperties(Model, properties);
}

Remove need to set up properties.


var _ = require('lodash')
var helper = require('./support/helper')
var assert = require('assert')

ogConsoleLog = console.log
var result = helper.run('example/test/lib/**/*.js'),
    log = helper.log()

assert.equal(result, false)
helper.assertLog(
  "TAP version 13",
  "1..3",
  "I'll run once before both tests",
  "I'll run twice - once before each test",
  "ok 1 - \"adds\" - test #1 in `example/test/lib/exporting-an-object.js`",
  "I'll run twice - once after each test",
  "I'll run twice - once before each test",
  "ok 2 - \"subtracts\" - test #2 in `example/test/lib/exporting-an-object.js`",
  "I'll run twice - once after each test",
  "I'll run once after both tests",
  "not ok 3 - \"blueIsRed\" - test #1 in `example/test/lib/single-function.js`",
  "  ---",
  "  message: 'blue' == 'red'",
  /  stacktrace: AssertionError: \'blue\' == \'red\'\n    at blueIsRed /,
  "  ..."
)

var _ = require('lodash')
var helper = require('./support/helper')
var assert = require('assert')

var result = helper.run('example/test/lib/**/*.js')

assert.equal(result, false)
helper.assertLog(
  "TAP version 13",
  "1..3",
  "I'll run once before both tests",
  "I'll run twice - once before each test",
  "ok 1 - \"adds\" - test #1 in `example/test/lib/exporting-an-object.js`",
  "I'll run twice - once after each test",
  "I'll run twice - once before each test",
  "ok 2 - \"subtracts\" - test #2 in `example/test/lib/exporting-an-object.js`",
  "I'll run twice - once after each test",
  "I'll run once after both tests",
  "not ok 3 - \"blueIsRed\" - test #1 in `example/test/lib/single-function.js`",
  "  ---",
  "  message: 'blue' == 'red'",
  /  stacktrace: AssertionError: 'blue' == 'red'/,
  "  ..."
)

Make the example test less erratic


var $ = require('../../lib/jquery');

describe('$', function() {
  describe('#param', function() {
    it('should return params', function() {
      var inst = $({navigator: {userAgent: ''}}, '');
      inst.$.param({foo: ' bar', baz: 'bat='}).should.equal('foo=%20bar&baz=bat%3D');
    });
  });
});

var $ = require('../../lib/jquery'),
    Cheerio = require('cheerio'),
    dom = require('../../lib/dom');

describe('$', function() {
  var window,
      inst;
  beforeEach(function() {
    window = {};
    dom.document(window);
    dom.navigator(window, {userAgent: ''});
    inst = $(window, '');
  });

  describe('document object', function() {
    it('should accept document object', function() {
      inst.$(window.document).should.be.instanceOf(Cheerio);
      inst.$('document').should.be.instanceOf(Cheerio);
    });
    describe('#ready', function() {
      it('should work on query', function(done) {
        inst.$('document').ready(done);
      });
      it('should work on literal', function(done) {
        inst.$(window.document).ready(done);
      });
    });
    it('should fake event binding', function() {
      var doc = inst.$(window.document);
      doc.on('foo', function(){}).should.equal(doc);
      doc.off('foo', function(){}).should.equal(doc);
    });
  });
  describe('window object', function() {
    it('should accept window object', function() {
      inst.$(window).should.be.instanceOf(Cheerio);
    });
    it('should fake event binding', function() {
      var doc = inst.$(window);
      doc.on('foo', function(){}).should.equal(doc);
      doc.off('foo', function(){}).should.equal(doc);
    });
  });

  describe('#param', function() {
    it('should return params', function() {
      inst.$.param({foo: ' bar', baz: 'bat='}).should.equal('foo=%20bar&baz=bat%3D');
    });
  });
});

Add test for various $ root cases


module.exports = {
    className: 'submit',
    template: require('./index.html'),
    data: {
        name: 'Submit',
        icon: '/images/blocks_submit.png',
        attributes: {
            innerHTML: {
                label: 'Button Text',
                type: 'string',
                value: 'Submit',
                skipAutoRender: true
            },
            color: {
                label: 'Button Color',
                type: 'color',
                value: '#32B2D2',
                skipAutoRender: true
            }
        }
    },
    methods: {
        save: function(e) {
            var self = this;

            self.$dispatch('dataSave');
        }
    },
    created: function() {
        var self = this;

        self.$on('dataSaveSuccess', function() {
            self.$el.querySelector('button').disabled = 'disabled';
            self.$el.querySelector('button').style.pointerEvents = 'none';
            self.$el.querySelector('button').innerHTML = 'Data submitted!';
        });
    }
};

module.exports = {
    className: 'submit',
    template: require('./index.html'),
    data: {
        name: 'Submit',
        icon: '/images/blocks_submit.png',
        attributes: {
            innerHTML: {
                label: 'Button Text',
                type: 'string',
                value: 'Submit',
                skipAutoRender: true
            },
            color: {
                label: 'Button Color',
                type: 'color',
                value: '#32B2D2',
                skipAutoRender: true
            }
        }
    },
    methods: {
        save: function (e) {
            var self = this;

            self.$dispatch('dataSave');
        }
    },
    created: function () {
        var self = this;

        self.$on('dataSaveSuccess', function () {
            self.$el.querySelector('button').disabled = 'disabled';
            self.$el.querySelector('button').style.pointerEvents = 'none';
            self.$el.querySelector('button').innerHTML = 'Data submitted!';
        });
    }
};

Resolve style errors on submit block


$(document).ready(function () {
	$("table.sortable").stupidtable();

	$(document).on("click", "table tr", function (evt) {
		var url = $(this).attr("data-url"),
			type = $(this).attr("data-type"),
			links = $(this).find("a");

		if (typeof url === "undefined" || typeof type === "undefined")
			return;

		evt.preventDefault();

		if (type === "folder" || type === "parent-folder") {
			window.location.href = url;
		}
		else if (type === "image") {
			links.first().ekkoLightbox({
				always_show_close: false,
				loadingMessage: '
Loading...
'
			});
		} else {
			ga('send', 'event', 'Download', type, url);
			window.location.href = url;
		}
	});
});

$(document).ready(function () {
	$("table.sortable").stupidtable();

	$(document).on("click", "table tr", function (evt) {
		var url = $(this).attr("data-url"),
			type = $(this).attr("data-type"),
			links = $(this).find("a");

		if (typeof url === "undefined" || typeof type === "undefined")
			return;

		evt.preventDefault();

		if (type === "folder" || type === "parent-folder") {
			window.location.href = url;
		}
		else if (type === "image") {
			links.first().ekkoLightbox({
				always_show_close: false,
				loadingMessage: '
Loading...
'
			});
		} else if (typeof(ga) != "undefined"){
			ga('send', 'event', 'Download', type, url);
			window.location.href = url;
		}
	});
});

Check that GA is defined


export default class ExpertUtilsService {
  constructor($uibModal) {
    this.$uibModal = $uibModal;
  }

  showRequest(expertRequest) {
    return this.$uibModal.open({
      component: 'expertContractDetails',
      size: 'lg',
      resolve: {
        expertRequest: expertRequest,
      }
    });
  }

  createBid(expertRequest) {
    return this.$uibModal.open({
      component: 'expertBidCreateDialog',
      size: 'lg',
      resolve: {
        expertRequest: expertRequest,
      }
    });
  }
}

export default class ExpertUtilsService {
  constructor($uibModal) {
    this.$uibModal = $uibModal;
  }

  showRequest(expertRequest) {
    return this.$uibModal.open({
      component: 'expertRequestDetailsDialog',
      size: 'lg',
      resolve: {
        expertRequest: expertRequest,
      }
    });
  }

  createBid(expertRequest) {
    return this.$uibModal.open({
      component: 'expertBidCreateDialog',
      size: 'lg',
      resolve: {
        expertRequest: expertRequest,
      }
    });
  }
}

Fix opening of contract preview dialog


import THREE from 'three.js';

const geometry = new THREE.CylinderGeometry( 0, 0.05, 0.3, 3 );

geometry.rotateX( Math.PI / 2 );
geometry.computeFaceNormals();
geometry.computeVertexNormals();

const material = new THREE.MeshBasicMaterial({
  blending: THREE.AdditiveBlending,
  color: '#aaf',
  transparent: true,
  opacity: 0.4
})

export class Flame extends THREE.Mesh {
  constructor() {
    super( geometry, material );

    this.scale.setLength( THREE.Math.randFloat( 0.5, 1 ) );
    this.rotation.z = 2 * Math.PI * Math.random();
  }
}

export default class Engine extends THREE.Group {
  constructor( source, count = 12 ) {
    super();

    this.source = source;

    let i = count;
    while( i-- ) {
      this.add( new Flame() );
    }
  }

  update() {
    this.children.forEach( flame => {
      flame.position.set(
        THREE.Math.randFloatSpread( 0.03 ),
        THREE.Math.randFloatSpread( 0.03 ),
        THREE.Math.randFloatSpread( 0.03 )
      );
    })
  }
}

import THREE from 'three.js';

const geometry = new THREE.CylinderGeometry( 0, 0.04, 0.25, 5 );

geometry.rotateX( Math.PI / 2 );
geometry.computeFaceNormals();
geometry.computeVertexNormals();

const material = new THREE.MeshLambertMaterial({
  blending: THREE.AdditiveBlending,
  color: '#fff',
  emissive: '#f43',
  transparent: true,
  opacity: 0.4
})

export class Flame extends THREE.Mesh {
  constructor() {
    super( geometry, material );
  }
}

export default class Engine extends THREE.Group {
  constructor( source, count = 3 ) {
    super();

    this.source = source;

    let i = count;
    while( i-- ) {
      this.add( new Flame() );
    }
  }

  update() {
    this.children.forEach( flame => {
      flame.position.set(
        THREE.Math.randFloatSpread( 0.005 ),
        THREE.Math.randFloatSpread( 0.005 ),
        THREE.Math.randFloatSpread( 0.03 )
      );

      flame.rotation.z = 2 * Math.PI * Math.random();
      flame.scale.setLength( THREE.Math.randFloat( 0.5, 1 ) );
    })
  }
}

Tweak Engine effect and coloration.


BookShelf.LaterList = function(params) {

    return BookShelf.BookList($.extend(params, {
        status: BookShelf.db.bookStatus.later,
        filter: function(book) {
            return !book.finishDate;
        },
        sort: [
            function(book) {
                return BookShelf.db.getBookStatus(book);
            },
            { getter: "startDate" }
        ]
    }));

};

BookShelf.LaterList = function(params) {

    return BookShelf.BookList($.extend(params, {
        status: BookShelf.db.bookStatus.later,
        filter: function(book) {
            return !book.finishDate;
        },
        sort: [
            function(book) {
                return BookShelf.db.getBookStatus(book);
            },
            { getter: "startDate" },
            { getter: "title" }
        ]
    }));

};

Improve sorting options in To Read view


$(document).ready( function () {
  $(".upvote").click( function(event) {
    var responseId = $(this).data("id");
    event.preventDefault();
    $.ajax({
      url: '/response/up_vote',
      method: 'POST',
      data: { id: responseId },
      dataType: 'JSON'
    }).done( function (voteCount) {
      if (voteCount == 1) {
        $("span[data-id=" + responseId + "]").html(voteCount + " vote");
      } else {
        $("span[data-id=" + responseId + "]").html(voteCount + " vote");
      }
    }).fail( function (voteCount) {
        console.log("Failed. Here is the voteCount:");
        console.log(voteCount);
    })
  })
})

$(document).ready( function () {
  $(".upvote").click( function(event) {
    var responseId = $(this).data("id");
    event.preventDefault();
    $.ajax({
      url: '/response/up_vote',
      method: 'POST',
      data: { id: responseId },
      dataType: 'JSON'
    }).done( function (voteCount) {
      if (voteCount == 1) {
        $("span[data-id=" + responseId + "]").html(voteCount + " vote");
      } else {
        $("span[data-id=" + responseId + "]").html(voteCount + " vote");
      }
    }).fail( function (failureResponse) {
        alert("You already voted for this response.");
        console.log(failureResponse);
    })
  })
})

Change failure to an alert message.


// react/prop-types rule is ignored because of this issue: https://github.com/yannickcr/eslint-plugin-react/issues/9
/* eslint react/prop-types: 0 */

import { compose, createStore, applyMiddleware, combineReducers } from 'redux';
import thunkMiddleware from 'redux-thunk';
import loggerMiddleware from '../middlewares/loggerMiddleware';
import reducers from '../reducers';
import { initalStates } from '../reducers';

export default props => {
  const initialComments = props;
  const { $$commentsState } = initalStates;
  const initialState = {
    $$commentsStore: $$commentsState.merge({
      $$comments: initialComments,
    }),
  };

  const reducer = combineReducers(reducers);
  const composedStore = compose(
    applyMiddleware(thunkMiddleware, loggerMiddleware)
  );

  return composedStore(createStore)(reducer, initialState);
};

Add comment to explain disabled eslint rule


/**
 * Shows arbitrary text in a news ticker-like fashion.
 *
 * The widget is designed to have a very small height in comparison to its width. This can be
 * achieved by increasing the granularity of the widget sizes in the config, e.g. by setting the
 * `dim[1]` of the grid to a high value and scaling accordingly the heights of the existing
 * widgets.
 *
 * Expects data from sources as simple strings.
 */
Dashboard.TickerWidget = Dashboard.Widget.extend({
  sourceData: "",

  templateName: 'ticker_widget',
  classNames: ['widget', 'widget-ticker'],

  widgetView: function() {
    var widget = this;

    return this._super().reopen({
      didInsertElement: function() {
        var scaleFactor = 0.7;
        var widgetHeight = this.$().height();

        var fontSize = widgetHeight * scaleFactor;

        var widgetUnitWidth = (DashboardConfig.grid.width - DashboardConfig.widgetMargins) /
          DashboardConfig.dim[0] - DashboardConfig.widgetMargins;

        var widgetWidth = widgetUnitWidth * widget.get('sizex') +
          DashboardConfig.widgetMargins * (widget.get('sizex') - 1) - 5;

        this.$().css('font-size', fontSize + 'px');
        this.$('.marquee').css('max-width', widgetWidth + 'px');
      }
    });
  }.property()
});

Add documentation for ticker widget


'use strict';

angular.module('quakeStatsApp').
    filter('WeaponsFilter', function () {
        var resultToName = {
			1: 'SHOTGUN',
			2: 'GAUNTLET',
			3: 'MACHINEGUN',
			4: 'GRENADE',
			6: 'ROCKET',
			7: 'ROCKET SPLASH',
			8: 'PLASMA',
			9: 'PLASMA SPLASH',
			10: 'RAILGUN',
			11: 'LIGHTNING',
			12: 'BFG',
			13: 'BFG SPLASH',
			18: 'TELEFRAG'
        };
        return function (input) {
            return resultToName[input];
        };
    });

'use strict';

angular.module('quakeStatsApp').
    filter('WeaponsFilter', function () {
        var resultToName = {
			1: 'SHOTGUN',
			2: 'GAUNTLET',
			3: 'MACHINEGUN',
			4: 'GRENADE',
			5: 'GRENADE SPLASH',
			6: 'ROCKET',
			7: 'ROCKET SPLASH',
			8: 'PLASMA',
			9: 'PLASMA SPLASH',
			10: 'RAILGUN',
			11: 'LIGHTNING',
			12: 'BFG',
			13: 'BFG SPLASH',
			18: 'TELEFRAG'
        };
        return function (input) {
            return resultToName[input];
        };
    });

Add the GRENADE SPLASH kill mode


/*!
{
  "name": "Backdrop Filter",
  "property": "backdropfilter",
  "tags": ["css"]
}
!*/
define(['Modernizr', 'testAllProps'], function(Modernizr, testAllProps) {
  Modernizr.addTest('backdropfilter', testAllProps('backdropFilter'));
});

/*!
{
  "name": "Backdrop Filter",
  "property": "backdropfilter",
  "authors": ["Brian Seward"],
  "tags": ["css"],
  "notes": [
    {
      "name": "W3C Editor’s Draft specification",
      "href": "http://dev.w3.org/fxtf/filters-2/#BackdropFilterProperty"
    },
    {
      "name": "Caniuse for CSS Backdrop Filter",
      "href": "http://caniuse.com/#feat=css-backdrop-filter"
    },
    {
      "name": "WebKit Blog introduction + Demo",
      "href": "https://www.webkit.org/blog/3632/introducing-backdrop-filters/"
    }
  ]
}
!*/
/* DOC
Detects support for CSS Backdrop Filters, allowing for background blur effects like those introduced in iOS 7. Support for this was added to iOS Safari/WebKit in iOS 9.
*/
define(['Modernizr', 'testAllProps'], function(Modernizr, testAllProps) {
  Modernizr.addTest('backdropfilter', testAllProps('backdropFilter'));
});

Add author, doc, notes to backdrop test


const lru = require('lru-cache');

module.exports = class LruCache {
  constructor({ maxItems }) {
    this.cache = lru(maxItems);
  }

  get(key) {
    return Promise.resolve(this.cache.get(key));
  }

  set(key, value, options) {
    const { maxAge } = options || {};
    this.cache.set(key, value, maxAge);
    return Promise.resolve(key);
  }

  values() {
    return Promise.resolve(this.cache.values());
  }

  length() {
    return this.cache.length;
  }
};

const lru = require('lru-cache');

module.exports = class LruCache {
  constructor(options) {
    const maxItems = options && options.maxItems
    this.cache = lru(maxItems);
  }

  get(key) {
    return Promise.resolve(this.cache.get(key));
  }

  set(key, value, options) {
    const { maxAge } = options || {};
    this.cache.set(key, value, maxAge);
    return Promise.resolve(key);
  }

  values() {
    return Promise.resolve(this.cache.values());
  }

  length() {
    return this.cache.length;
  }
};

Fix error if no argument is passed to constructor


$(function() {
	// https://github.com/naugtur/insertionQuery
	var anime_watch = function(selector, callback) {
		var guid = selector.replace(/[^a-zA-Z0-9]+/g, "_") +"_"+ ((new Date()).getTime());

		$("<style/>").html([
			"@-webkit-keyframes {guid} { from { clip: rect(auto, auto, auto, auto); } to { clip: rect(auto, auto, auto, auto); } }",
			"@keyframes {guid} { from { clip: rect(auto, auto, auto, auto); } to { clip: rect(auto, auto, auto, auto); } }",
			"{selector} { animation-duration: 0.001s; animation-name: {guid}; -webkit-animation-duration: 0.001s; -webkit-animation-name: {guid}; }"
		].join("\n").replace(/\{guid\}/g, guid).replace(/\{selector\}/g, selector)).appendTo("head");

		var eventHandler = function(event) {
			if (event.animationName === guid || event.WebkitAnimationName === guid) {
				callback.call(event.target, event.target);
			}
		}

		document.addEventListener("animationstart", eventHandler, false);
		document.addEventListener("webkitAnimationStart", eventHandler, false);
	};

	// Watches the animation event and parses li if they are inserted
	anime_watch("span.message_content", function(span) {
		$(span).parseIssueTrackerNumbers();
	});
});

$(function() {
	// https://github.com/naugtur/insertionQuery
	var anime_watch = function(selector, callback) {
		var guid = selector.replace(/[^a-zA-Z0-9]+/g, "_") +"_"+ ((new Date()).getTime());

		$("<style/>").html([
			"@-webkit-keyframes {guid} { from { clip: rect(auto, auto, auto, auto); } to { clip: rect(auto, auto, auto, auto); } }",
			"@keyframes {guid} { from { clip: rect(auto, auto, auto, auto); } to { clip: rect(auto, auto, auto, auto); } }",
			"{selector} { animation-duration: 0.001s; animation-name: {guid}; -webkit-animation-duration: 0.001s; -webkit-animation-name: {guid}; }"
		].join("\n").replace(/\{guid\}/g, guid).replace(/\{selector\}/g, selector)).appendTo("head");

		var eventHandler = function(event) {
			if (event.animationName === guid || event.WebkitAnimationName === guid) {
				callback.call(event.target, event.target);
			}
		}

		document.addEventListener("animationstart", eventHandler, false);
		document.addEventListener("webkitAnimationStart", eventHandler, false);
	};

	// Watches the animation event and parses li if they are inserted
	anime_watch("div.message_content", function(div) {
		$(div).parseIssueTrackerNumbers();
	});
});

Fix for new version of slack


// @flow
import React, { Component } from 'react';
import {connect} from 'react-redux'
import path from 'path'
import Catalog from '../components/Catalog';
import FileAdder from '../components/FileAdder';
import {addFile} from '../actions/actions'

class HomePage extends Component {

  handleFileCopied = relFilePath => {
    const {dispatch, settings} = this.props
    const fakeFstat = {
      isDirectory: () => false
    }
    const absPath = [settings.path, relFilePath].join(path.sep)
    dispatch(addFile(absPath, relFilePath, fakeFstat))
  }

  render() {
    const {allPages, settings} = this.props
    return (

        <FileAdder
            basePath={settings.path}
            onFileCopied={this.handleFileCopied} />
        <Catalog allPages={allPages} />

    );
  }
}

class Header extends Component {
  render() {
    return
Head

  }
}

function mapStateToProps(state) {
  const allPages = []
  const {settings} = state
  Object.keys(state.catalog).forEach(book => {
    Object.keys(state.catalog[book]).forEach(page => {
      const pageObject = {
        ...state.catalog[book][page],
        book,
        page,
      }
      allPages.push(pageObject)
    })
  })
  return {allPages, settings}
}

export default connect(mapStateToProps)(HomePage)

// @flow
import React, { Component } from 'react';
import {connect} from 'react-redux'
import path from 'path'
import Catalog from '../components/Catalog';
import FileAdder from '../components/FileAdder';
import {addFile} from '../actions/actions'

class HomePage extends Component {

  handleFileCopied = relFilePath => {
    const {dispatch, settings} = this.props
    const fakeFstat = {
      isDirectory: () => false
    }
    const absPath = [settings.path, relFilePath].join(path.sep)
    dispatch(addFile(absPath, relFilePath, fakeFstat))
  }

  render() {
    const {allPages, settings} = this.props
    return (
      <div>
        <FileAdder
            basePath={settings.path}
            onFileCopied={this.handleFileCopied} />
        <Catalog allPages={allPages} />
      </div>
    );
  }
}

function mapStateToProps(state) {
  const allPages = []
  const {settings} = state
  Object.keys(state.catalog).forEach(book => {
    Object.keys(state.catalog[book]).forEach(page => {
      const pageObject = {
        ...state.catalog[book][page],
        book,
        page,
      }
      allPages.push(pageObject)
    })
  })
  return {allPages, settings}
}

export default connect(mapStateToProps)(HomePage)

Remove the dummy header from home page.


'use strict'

import { CLI } from './cli'
import { run as runStartup, build } from './startup'

// so we can easily weed out console.logs
// print = keep
// console.log = delete
global.print = console.log.bind(console)

async function run(options) {
  const cli = new CLI()
  cli.activate()
  await runStartup(options, cli)
  cli.addCommand('build', 'Build dist files of your motion app', async function() {
    this.log('You asked me to build it')
  })
  return cli
}

// TODO: This is for backward compatibility only, remove this in the upcoming future
export default { build, run }

'use strict'

import chalk from 'chalk'
import { CLI } from './cli'
import { run as runStartup, build } from './startup'
import server from './server'

// so we can easily weed out console.logs
// print = keep
// console.log = delete
global.print = console.log.bind(console)

async function run(options) {
  const cli = new CLI()
  cli.activate()
  await runStartup(options, cli)

  // Startup messages
  cli.log(chalk.green('Server running at') + ' ' + chalk.yellow('http://' + server.url()))

  cli.addCommand('open', 'Open this project in Browser', async function() {
    this.log('I should have opened this project in browser')
  })
  cli.addCommand('editor', 'Open this project in Atom', async function() {
    this.log('I should have opened this project in Atom')
  })
  cli.addCommand('build', 'Build dist files of your motion app', async function() {
    this.log('I should have built dist files of this app')
  })
  return cli
}

// TODO: This is for backward compatibility only, remove this in the upcoming future
export default { build, run }

Add basic commands and welcome message to not in a motion app directory


/**
 * See http://mochajs.org/ for documentation.
 */
var expect  = require('expect.js'),
    request = require('supertest');

describe('/', function() {
  it('should redirect if we are not signed in', function(done) {
    var server = require('../core/app');
    request(server)
      .get('/')
      .expect(302, done);
  });
});

describe('/logout', function() {
  it('should redirect to the CAS logout page', function(done) {
    var server = require('../core/app');
    request(server)
      .get('/logout')
      .expect('Location', 'https://cas.byu.edu/cas/logout')
      .expect(302, done);
  });
});

/**
 * See http://mochajs.org/ for documentation.
 */
var expect  = require('expect.js'),
    request = require('supertest');

describe('/', function() {
  it('should redirect if we are not signed in', function(done) {
    var server = require('../core/app');
    request(server)
      .get('/')
      .expect(302, done);
  });
});

describe('/logout', function() {
  it('should redirect to the CAS logout page', function(done) {
    var server = require('../core/app');
    request(server)
      .get('/logout')
      .expect('Location', 'https://cas.byu.edu/cas/logout')
      .expect(302, done);
  });

  it('should redirect me to my desired location after logging out', function(done) {
    var server = require('../core/app');
    var url = "http://example.com";

    request(server)
      .get('/logout?service='+url)
      .end(function() {
        request(this.res.headers.location)
          .get('')
          .expect('Location', url, done);
      });
  });
});

Test showing that the server properly redirects after a logout.


$(function () {
  var isElementOnScreen = function($el, scrollTop) {
    // Get very bottom of element
    elementBottom = $el.offset().top + $el.outerHeight();
    // Get very top of element
    elementTop = $el.offset().top - scrollTop;

    if (elementTop <= $(window).height() && elementBottom - scrollTop >= 0) {
      // Element is on-screen
      return true;
    } else {
      // Element is not on-screen
      return false;
    }
  }

  $('.js-scroll-to').on('click', function(e) {
    e.preventDefault();

    $target = $(this).attr('href');
    $('body').animate({
      scrollTop: $target.offset().top
    });
  });

  // Scroll effect on steps
  $steps = $('.js-step, .js-learn-more');

  $(window).on('scroll', function() {
    $steps.each(function() {
      isOnScreen = isElementOnScreen($(this), ($(window).scrollTop() - 150));

      if (isOnScreen && !$(this).hasClass('is-visible')) {
        $(this).addClass('is-visible');
      }
    });
  });
});

$(function () {
  var isElementOnScreen = function($el, scrollTop) {
    // Get very bottom of element
    var elementBottom = $el.offset().top + $el.outerHeight();
    // Get very top of element
    var elementTop = $el.offset().top - scrollTop;

    if (elementTop <= $(window).height() && elementBottom - scrollTop >= 0) {
      // Element is on-screen
      return true;
    } else {
      // Element is not on-screen
      return false;
    }
  }

  $('.js-scroll-to').on('click', function(e) {
    e.preventDefault();

    var $target = $(this).attr('href');
    $('body').animate({
      scrollTop: $target.offset().top
    });
  });

  // Scroll effect on steps
  var $steps = $('.js-step, .js-learn-more');

  $(window).on('scroll', function() {
    $steps.each(function() {
      var isOnScreen = isElementOnScreen($(this), ($(window).scrollTop() - 150));

      if (isOnScreen && !$(this).hasClass('is-visible')) {
        $(this).addClass('is-visible');
      }
    });
  });
});

Make sure variables don't pollute the global namespace.


'use strict';
var path = require('path');
var argv = require('yargs').default('browser', true).argv;
var pkg = require('../package.json');

var dest = 'build';
var src = 'src';

var ENVIRONMENT = argv.env || process.env.env || 'development';
var APP_NAME = pkg.name;

module.exports = {
  environment: ENVIRONMENT,
  license: {
    src: '../LICENSE'
  },
  tests: {
    failTask: false,
    forceKarmaExit: {
      onSuccess: true,
      onFailure: true
    }
  },
  src: src,
  jsdoc: {
    src: src,
    readme: './README.md',
    dest: './docs',
    template: './node_modules/ink-docstrap/template',
    configFile: './gulpfile.js/doc_config.js'
  },
  lint: {
    src: [
      path.join(src, '**', '*.js'),
    ]
  },
  browserify: {
    // A separate bundle will be generated for each
    // bundle config in the list below
    debug: true, // debug mode for browserify (sourcemaps)
    watch: false, // should we use watchify
    bundleConfigs: [
      {
        entries: 'index.js',
        dest: dest,
        outputName: APP_NAME + '.js'
      }
    ]
  },
  // @TODO what 'production' means in this context is not obvious.
  // I.e. why are no other environments defined?
  production: {
    jsSrc: path.join(dest, '**', '*.js'),
    dest: dest
  }
};

'use strict';
var path = require('path');
var argv = require('yargs').default('browser', true).argv;
var pkg = require('../package.json');

var dest = 'build';
var src = 'src';
var tests = 'tests';

var ENVIRONMENT = argv.env || process.env.env || 'development';
var APP_NAME = pkg.name;

module.exports = {
  environment: ENVIRONMENT,
  license: {
    src: '../LICENSE'
  },
  tests: {
    failTask: false,
    forceKarmaExit: {
      onSuccess: true,
      onFailure: true
    }
  },
  src: src,
  jsdoc: {
    src: src,
    readme: './README.md',
    dest: './docs',
    template: './node_modules/ink-docstrap/template',
    configFile: './gulpfile.js/doc_config.js'
  },
  lint: {
    src: [
      path.join(src, '**', '*.js'),
      path.join(tests, '**', '*.js'),
    ]
  },
  browserify: {
    // A separate bundle will be generated for each
    // bundle config in the list below
    debug: true, // debug mode for browserify (sourcemaps)
    watch: false, // should we use watchify
    bundleConfigs: [
      {
        entries: 'index.js',
        dest: dest,
        outputName: APP_NAME + '.js'
      }
    ]
  },
  // @TODO what 'production' means in this context is not obvious.
  // I.e. why are no other environments defined?
  production: {
    jsSrc: path.join(dest, '**', '*.js'),
    dest: dest
  }
};

Add tests folder to lint task


import Bacon from 'baconjs'
import R from 'ramda'
import clone from 'clone'
import slug from 'slug'
import serviceTemplate from '../../service.json'

export default function createServiceOrTakeExisting(image, buildSetting, tutum) {
  return listAllServices(tutum)
    .flatMap((services) => {
      let findService = R.find(R.propEq('image_name', `${image.name}:${buildSetting.tag}`))
      let service = findService(services)

      if (service) {
        return service
      }

      let options = clone(serviceTemplate, false)

      options.image = `${image.name}:${buildSetting.tag}`
      options.name = slug(buildSetting.branch)
      options.container_envvars.push({
        key: 'VIRTUAL_PATH',
        value: slug(buildSetting.branch)
      })

      return createService(options, tutum)
    })
}

function listAllServices(tutum) {
  return Bacon.fromPromise(tutum.listServices())
}

function createService(options, tutum) {
  return Bacon.fromPromise(tutum.createService(options))
}

import Bacon from 'baconjs'
import R from 'ramda'
import clone from 'clone'
import slug from 'slug'
import serviceTemplate from '../../service.json'
import colors from 'colors'

let debug = require('debug')('tutum-tagger')

export default function createServiceOrTakeExisting(image, buildSetting, tutum) {
  return listAllServices(tutum)
    .flatMap((services) => {
      let imageWithTag = `${image.name}:${buildSetting.tag}`
      let findService = R.find(R.propEq('image_name', imageWithTag))
      let service = findService(services.objects)

      if (service) {
        debug(`Service based on ${imageWithTag.cyan} already exists.`)
        return service
      }

      let options = clone(serviceTemplate, false)

      options.image = imageWithTag
      options.name = slug(buildSetting.branch)
      options.container_envvars.push({
        key: 'VIRTUAL_PATH',
        value: slug(buildSetting.branch)
      })

      debug(`Creating a new service based on ${imageWithTag.cyan}.`)

      return createService(options, tutum)
    })
}

function listAllServices(tutum) {
  return Bacon.fromPromise(tutum.listServices())
}

function createService(options, tutum) {
  return Bacon.fromPromise(tutum.createService(options))
}

Fix the findService function, add debug logs


const fs = require('fs'),
    path = require('path'),
    expect = require('chai').expect,

    CACHE_DIR = path.join(__dirname, '/../../.cache'),
    THRESHOLD = 3.7 * 1024 * 1024; // 3.7 MB

describe('bootcode size', function () {
    this.timeout(60 * 1000);

    it('should not exceed the threshold', function (done) {
        fs.readdir(CACHE_DIR, function (err, files) {
            if (err) { return done(err); }

            files.forEach(function (file) {
                var size = fs.statSync(CACHE_DIR + '/' + file).size;

                expect(size, (file + ' threshold exceeded')).to.be.below(THRESHOLD);
            });

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

const fs = require('fs'),
    path = require('path'),
    expect = require('chai').expect,

    CACHE_DIR = path.join(__dirname, '/../../.cache'),
    THRESHOLD = 2.5 * 1024 * 1024; // 2.5 MB

describe('bootcode size', function () {
    this.timeout(60 * 1000);

    it('should not exceed the threshold', function (done) {
        fs.readdir(CACHE_DIR, function (err, files) {
            if (err) { return done(err); }

            files.forEach(function (file) {
                var size = fs.statSync(CACHE_DIR + '/' + file).size;

                expect(size, (file + ' threshold exceeded')).to.be.below(THRESHOLD);
            });

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

Update bootcode threshold to 2.5 MB


import React from 'react';
import * as GifActions from '../../actions/GifActions';

class GifFilter extends React.Component {
    constructor(props) {
        super(props);

        this.handleFilterChange = this.handleFilterChange.bind(this);
    }
    handleFilterChange(e) {
        GifActions.filter(e.target.value);
    }
    render() {
        return (

                    <input type="text" name="filter" className="form-control" placeholder="Filter by name" onChange={this.handleFilterChange} />

        );
    }
};

export default GifFilter;

import React from 'react';
import GifAddModal from '../common/GifAddModal'
import * as GifActions from '../../actions/GifActions';

class GifFilter extends React.Component {
    constructor(props) {
        super(props);

        this.handleFilterChange = this.handleFilterChange.bind(this);
        this.handleAdd = this.handleAdd.bind(this);
    }
    handleFilterChange(e) {
        GifActions.filter(e.target.value);
    }
    handleAdd() {
        $('#GifAddModal').modal('show');
    }
    render() {
        return (

                        <input type="text" name="filter" className="form-control" placeholder="Filter by name" onChange={this.handleFilterChange} />

                            <button className="btn btn-default" onClick={this.handleAdd}>

                            </button>

                <GifAddModal />

        );
    }
};

export default GifFilter;

Add AddModal in filter form


import React from 'react';
import ReactList from 'react-list';
import {
  Col,
  ListGroup,
  Panel,
} from 'react-bootstrap';

export function renderResults(title, render) {
  return (
    <Col lg={12} md={12} sm={12} xs={12}>
      <Panel style={{marginTop: 15,}} collapsible defaultExpanded header={`${title} (${render.length})`}>
        <ListGroup fill style={{maxHeight: '100vh', overflow: 'auto',}}>
          <ReactList
            itemRenderer={i => render[i]}
            length={render.length}
            minSize={10}
          />
        </ListGroup>
      </Panel>
    </Col>
  );
}

import React from 'react';
import ReactList from 'react-list';
import {
  Col,
  ListGroup,
  Panel,
} from 'react-bootstrap';

export function renderResults(title, render) {
  return (
    <Col lg={12} md={12} sm={12} xs={12}>
      <Panel style={{marginTop: 20,}} collapsible defaultExpanded header={`${title} (${render.length})`}>
        <ListGroup fill style={{maxHeight: '100vh', overflow: 'auto',}}>
          <ReactList
            itemRenderer={i => render[i]}
            length={render.length}
            minSize={10}
          />
        </ListGroup>
      </Panel>
    </Col>
  );
}

Make spacing consistent with vertical spacing between panels


var http = require('http'),
	fs = require('fs'),
	util = require('./util')

// require.paths.unshift('.')

var modules = {},
	port = 1234,
	host = 'localhost'

var server = http.createServer(function(req, res) {
	if (req.url.match(/\.js$/)) {
		fs.readFile(req.url, function(err, content) {
			if (err) { return res.end('alert("' + err + '")') }
			// TODO prefix with a (function() { ... })()
			res.end(content)
		})
	} else {
		// main module
		var modulePath = __dirname + req.url + '.js'
		var deps = util.getDependencyList(modulePath),
			base = '//' + host + ':' + port

		res.write('var require = {_:{}}\n')
		for (var i=0; i<deps.length; i++) {
			var path = base + deps[i]
			res.write('document.write(\'<script src="'+path+'"></script>\')\n')
		}
		res.end()
	}
})
server.listen(port, host)

var http = require('http'),
	fs = require('fs'),
	util = require('./util')

// require.paths.unshift('.')

var modules = {},
	port = 1234,
	host = 'localhost'

var closureStart = '(function() {',
	moduleDef = 'var module = {exports:{}}; var exports = module.exports;',
	closureEnd = '\n})()'

moduleDef += 'var require = function() { return {} };'

var server = http.createServer(function(req, res) {
	if (req.url.match(/\.js$/)) {
		fs.readFile(req.url, function(err, content) {
			if (err) { return res.end('alert("' + err + '")') }
			// TODO rewrite require calls to require._["<MODULE PATH>"]
			res.write(closureStart + moduleDef)
			res.write(content)
			res.end(closureEnd)
		})
	} else {
		// main module
		var modulePath = __dirname + req.url + '.js'
		var deps = util.getDependencyList(modulePath),
			base = '//' + host + ':' + port

		res.write('var require = {_:{}}\n')
		for (var i=0; i<deps.length; i++) {
			var path = base + deps[i]
			res.write('document.write(\'<script src="'+path+'"></script>\')\n')
		}
		res.end()
	}
})
server.listen(port, host)

Write out the content of each file in a closure with module and exports defined


var Foo = {
 value : function() { return this.x + this.y; }
};

var a = { prototype: Foo, x: 1, y: 2 };
var b = new Foo();
b.x = 2;
b.y = 3;

var result1 = a.value();
var result2 = b.value();
result = result1==3 && result2==5;

function Foo (x,y)
{
    this.x = x;
    this.y = y;
}

Foo.prototype.value = function() { return this.x + this.y; }

var a = new Foo(1,2);
var b = new Foo(2,3);

var result1 = a.value();
var result2 = b.value();
result = result1==3 && result2==5;

Test modified to be correct Javascript.


/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    baseURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

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

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

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

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

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

  if (environment === 'production') {

  }

  return ENV;
};

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    baseURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

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

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

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

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

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

  if (environment === 'production') {
    ENV.baseURL = 'http://baptiste.meurant.io/ember-array-contains-helper'
  }

  return ENV;
};

Set base URL for dummy app production


// Install ES2015-complaint polyfills for Object, Array, String, Function,
// Symbol, Map, Set, and Promise, patching the native implementations when
// they are available.
require("meteor-ecmascript-runtime");
require("./install-promise.js");

// Verify that the babel-runtime package is available to be required.
// The .join("/") prevents babel-plugin-transform-runtime from
// "intelligently" converting this to an import statement.
var regenerator = require([
  "babel-runtime",
  "regenerator"
].join("/"));

// Use Promise.asyncApply to wrap calls to runtime.async so that the
// entire async function will run in its own Fiber, not just the code that
// comes after the first await.
var realAsync = regenerator.async;
regenerator.async = function () {
  return Promise.asyncApply(realAsync, regenerator, arguments);
};

// Install global.meteorBabelHelpers so that the compiler doesn't need to
// add boilerplate at the top of every file.
require("meteor-babel").defineHelpers();

// Installs source map support with a hook to add functions to look for
// source maps in custom places.
require('./source-map-retriever-stack.js');

// Install ES2015-complaint polyfills for Object, Array, String, Function,
// Symbol, Map, Set, and Promise, patching the native implementations when
// they are available.
require("meteor-ecmascript-runtime");
require("./install-promise.js");

// Verify that the babel-runtime package is available to be required.
// The .join("/") prevents babel-plugin-transform-runtime from
// "intelligently" converting this to an import statement.
var regenerator = require([
  "babel-runtime",
  "regenerator"
].join("/"));

// Use Promise.asyncApply to wrap calls to runtime.async so that the
// entire async function will run in its own Fiber, not just the code that
// comes after the first await.
var realAsync = regenerator.async;
regenerator.async = function () {
  return Promise.asyncApply(realAsync, regenerator, arguments);
};

// Install global.meteorBabelHelpers so that the compiler doesn't need to
// add boilerplate at the top of every file.
require("meteor-babel").defineHelpers();

var Mp = module.constructor.prototype;
var moduleLoad = Mp.load;
Mp.load = function (filename) {
  var result = moduleLoad.apply(this, arguments);
  var runSetters = this.runSetters || this.runModuleSetters;
  if (typeof runSetters === "function") {
    // Make sure we call module.runSetters (or module.runModuleSetters, a
    // legacy synonym) whenever a module finishes loading.
    runSetters.call(this);
  }
  return result;
};

// Installs source map support with a hook to add functions to look for
// source maps in custom places.
require('./source-map-retriever-stack.js');

Call module.runSetters() after any meteor/tools modules load.


var BookLookup = { }

BookLookup.find_by_isbn = function(isbn, callback) {
  $.getJSON('/books/isbn/'+ encodeURI(isbn), callback);
}

BookLookup.find_and_render = function(isbn) {
  if (isbn == "") {
    $('#book-placeholder').html('');
    return;
  }

  $('#book-placeholder').addClass('loading').html('Searching for book...');
  BookLookup.find_by_isbn(isbn, function(data){
    output = Mustache.render($('#book-preview-template').html(), data);
    $('#book-placeholder').removeClass('loading').html( output );
  });
}

$(document).ready(function() {
  var book_lookup_timeout;
  var previous_query = '';

  $('#new_book input[name=commit]').remove();

  $('#book_isbn').keyup( function(e) {
    var isbn = $('#book_isbn').val();

    if (isbn !== previous_query) {
      $('#book-placeholder').addClass('loading').html('Searching for book...');

      clearTimeout(book_lookup_timeout);
      book_lookup_timeout = setTimeout(function() {
        previous_query = isbn;
        BookLookup.find_and_render(isbn);
      }, 500);
    }
  });

  var isbn = $('#book_isbn').val();
  if (isbn != '') {
    BookLookup.find_and_render(isbn);
  }
});

var BookLookup = { }

BookLookup.find_by_isbn = function(isbn, success, failure) {
  $.getJSON('/books/isbn/'+ encodeURI(isbn), function(data) {
    if (data.title !== null) {
      success(data);
    } else {
      failure();
    }
  });
}

BookLookup.find_and_render = function(isbn) {
  if (isbn == "") {
    $('#book-placeholder').html('');
    return;
  }

  $('#book-placeholder').addClass('loading').html('Searching for book...');
  BookLookup.find_by_isbn(isbn, function(data){
    output = Mustache.render($('#book-preview-template').html(), data);
    $('#book-placeholder').removeClass('loading').html( output );
  }, function() {
    $('#book-placeholder').html( 'No book found for this ISBN.' );
  });
}

$(document).ready(function() {
  var book_lookup_timeout;
  var previous_query = '';

  $('#new_book input[name=commit]').remove();

  $('#book_isbn').keyup( function(e) {
    var isbn = $('#book_isbn').val();

    if (isbn !== previous_query) {
      $('#book-placeholder').addClass('loading').html('Searching for book...');

      clearTimeout(book_lookup_timeout);
      book_lookup_timeout = setTimeout(function() {
        previous_query = isbn;
        BookLookup.find_and_render(isbn);
      }, 500);
    }
  });

  var isbn = $('#book_isbn').val();
  if (isbn != '') {
    BookLookup.find_and_render(isbn);
  }
});

Handle cases where no book can be found


import Ember from 'ember';
import DS from 'ember-data';

export default Ember.Component.extend({
  currentUser: Ember.inject.service(),
  tagName: 'div',
  classNames: ['dashboard-block'],
  courseSorting: ['startDate:desc'],
  sortedListOfCourses: Ember.computed.sort('listOfCourses', 'courseSorting'),
  listOfCourses: Ember.computed('currentUser.model.allRelatedCourses.[]', function(){
    let defer = Ember.RSVP.defer();
    this.get('currentUser').get('model').then( user => {
      user.get('allRelatedCourses').then( courses => {
        let filteredCourses = courses.filter(course => {
          return (!course.get('locked') && !course.get('archived'));
        });
        defer.resolve(filteredCourses);
      });
    });
    return DS.PromiseArray.create({
      promise: defer.promise
    });
  }),
});

import Ember from 'ember';
import DS from 'ember-data';

const { Component, computed, inject, RSVP } = Ember;
const { service }= inject;

export default Component.extend({
  store: service(),
  currentUser: service(),
  tagName: 'div',
  classNames: ['dashboard-block'],
  courseSorting: ['startDate:desc'],
  sortedListOfCourses: computed.sort('listOfCourses', 'courseSorting'),
  listOfCourses: computed('currentUser.model', function(){
    let defer = RSVP.defer();
    this.get('currentUser').get('model').then( user => {
      this.get('store').query('course', {
        filters: {
          users: [user.get('id')],
          locked: false,
          archived: false
        }
      }).then(filteredCourses => {
        defer.resolve(filteredCourses);
      });
    });
    return DS.PromiseArray.create({
      promise: defer.promise
    });
  }),
});

Use courses query to load a users courses


////////////////////////////
// Form Validator factory //
////////////////////////////

'use strict';

pay.factory('FormValidator', [function () {
    return function (form, imageSelector, imageValidator) {
            // If we end directly the function, all errors may be not thrown
            var formValid = true;

            // Image validation
            if (typeof imageValidator !== 'undefined') {
                var $file = form[imageSelector];
                var file = $file.files[0];
                if (!file || (file.type !== 'image/png' && file.type !== 'image/jpeg')) {
                    $($file).parent().parent().next().addClass('ng-invalid');
                    formValid = false;
                } else {
                    $($file).parent().parent().next().removeClass('ng-invalid').addClass('ng-valid');
                }
            }

            // Input validation
            var isFormValid = $('.ng-pristine, .ng-invalid', form).filter(function () {
                return this.value.length === 0;
            }).length === 0;
            if (!isFormValid) {
                var $invalids = $('.ng-pristine, .ng-invalid', form).filter(function () {
                    return this.value.length === 0;
                });
                $invalids.removeClass('ng-pristine ng-valid').addClass('ng-invalid');
                formValid = false;
            }

            return formValid;
    };
}]);

////////////////////////////
// Form Validator factory //
////////////////////////////

'use strict';

pay.factory('FormValidator', [function () {
    return function (form, imageSelector, imageValidator) {
            var formValid = true;

            // Image validation
            if (typeof imageValidator !== 'undefined') {
                var $file = form[imageSelector];
                var file = $file.files[0];
                if (!file || (file.type !== 'image/png' && file.type !== 'image/jpeg')) {
                    $($file).parent().parent().next().addClass('ng-invalid');
                    formValid = false;
                } else {
                    $($file).parent().parent().next().removeClass('ng-invalid').addClass('ng-valid');
                }
            }

            // Input validation
            var $invalids = $('.ng-pristine, .ng-invalid', form);
            var emptyForms = $invalids.filter(function () {
                console.log('On a un pristine ou un invalide', this)
                if ($(this).is(':visible') === false) {
                    console.log('Bon ok il était pas visible le coco');
                    return false;
                }
                return this.value.length === 0;
            });
            console.log('Champs problématiques : ', emptyForms);
            if (emptyForms.length !== 0) {
                emptyForms.removeClass('ng-pristine ng-valid').addClass('ng-invalid');
                formValid = false;
            }

            return formValid;
    };
}]);

Fix form validator (lot cleaner)


this.speed = 30;
this.life = 3000;

this.update = function(time)
{
    if(this.Scene.paused)
        return;

    var addX = this.speed*(time/1000)

    this.GameObject.translate(0, addX, 0);

    this.life -= time;
    if(this.life <= 0)
        this.GameObject.destroy();
}

this.speed = 30;
this.life = 3000;

this.update = function(time)
{
    if(this.Scene.paused)
        return;

    var addY = this.speed*(time/1000)

    this.GameObject.translate(0, addY, 0);

    this.life -= time;
    if(this.life <= 0)
        this.GameObject.destroy();
}

Fix addX to addY since it deals with the vertical coordinate


!function($) {

  'use strict';

  /**
   * Soooooooo dirty ;_;
   * add the "detach from post" link
   */
  $(document).on('click', '.attachments-browser .attachment', function() {
    var html = 'Detach from the post'

    $('.attachment-details').find('.delete-attachment').parent().append(html)
  })

  /**
   * Remove the media from the post
   */
  $(document).on('click', '.remove-post-attachment', function(e) {
    e.preventDefault()
    // get the attachment id
    var datas = $(this).closest('.media-sidebar').find('input[type=hidden]').attr('name')
      , regex = /\[([0-9]*)\]/
      , match = regex.exec(datas)

    if (match !== null) {
      var id = match[1]

      $.ajax({
          url: '/wp-content/plugins/remove-post-attachment/ajax.php'
        , type: 'post'
        , dataType: 'json'
        , data: { id: id }
      })
      .done(function() {
        // quick and dirty: deselect the attachment and remove it from the list
        var attachment = $('.attachments').find('.attachment.selected')
        attachment.find('.check').click()
        attachment.remove()
      })
    }
  })

}(window.jQuery);

!function($) {

  'use strict';

  /**
   * Soooooooo dirty ;_;
   * add the "detach from post" link
   */
  $(document).on('click', '.attachments-browser .attachment', function() {
    var html = 'Detach from the post'

    $('.attachment-details').find('.delete-attachment').parent().append(html)
  })

  /**
   * Remove the media from the post
   */
  $(document).on('click', '.remove-post-attachment', function(e) {
    e.preventDefault()
    // get the attachment id
    var datas = $(this).closest('.media-sidebar').find('.edit-attachment').attr('href')
      , regex = /post=([0-9]*)&/g
      , match = regex.exec(datas)

    if (match !== null) {
      var id = match[1]

      $.ajax({
          url: '/wp-content/plugins/wordpress-remove-post-attachment/ajax.php'
        , type: 'post'
        , dataType: 'json'
        , data: { id: id }
      })
      .done(function() {
        // quick and dirty: deselect the attachment and remove it from the list
        var attachment = $('.attachments').find('.attachment.selected')
        attachment.find('.check').click()
        attachment.remove()
      })
    }
  })

}(window.jQuery);

Fix id source and ajax url


App.Router.map(function () {
  this.resource('projects', {path: '/'}, function(){
    this.resource('project', {path: '/project/:project_id'}, function(){
      this.resource('investigation', {path: 'investigations/:investigation_id'});
    });
  });
});

App.Router.reopen({
  rootURL: '/portal'
})

App.ProjectsRoute = Ember.Route.extend({
  model: function () {
    return this.store.find('project');
  },
  renderTemplate: function() {
    this.render('projects', {
      outlet: 'off-canvas'
    });
  }
});

App.ProjectRoute = Ember.Route.extend({
  model: function(params) {
    return this.store.find('project', params.project_id);
  },
  renderTemplate: function() {
    this.render('investigations', {
      outlet: 'main'
    });

    this.render({
      outlet: 'title'
    })
  }
});

App.Router.map(function () {
  this.resource('projects', {path: '/'}, function(){
    this.resource('project', {path: '/project/:project_id'}, function(){
      this.resource('investigation', {path: 'investigations/:investigation_id'});
    });
  });
});

App.Router.reopen({
  rootURL: '/portal'
})

App.ProjectsRoute = Ember.Route.extend({
  model: function () {
    return this.store.find('project');
  },
  afterModel: function(projects, transition) {
    if (projects.get('length') === 1) {
      this.transitionTo('project', projects.objectAt(0));
    }
  },
  renderTemplate: function() {
    this.render('projects', {
      outlet: 'off-canvas'
    });
  }
});

App.ProjectRoute = Ember.Route.extend({
  model: function(params) {
    return this.store.find('project', params.project_id);
  },

  renderTemplate: function() {
    this.render('investigations', {
      outlet: 'main'
    });

    this.render({
      outlet: 'title'
    })
  }
});

Load first project if only one project