'use strict'

const nearley = require('nearley')
const grammar = require('./grammar')

function byLevel(a, b) {
  return a.level < b.level ? -1 : a.level > b.level ? 1 : 0
}

function limit(results, { level, types }) {
  if (!results.length) return results
  if (typeof level !== 'number') level = 2

  return results.filter(res =>
    (level >= res.level) && (!types || types.includes(res.type)))
}

function best(results) {
  if (results.length < 2) return results[0]

  // If there are multiple results, pick the first
  // one on the lowest level!
  return results.sort(byLevel)[0]
}

module.exports = {

  parse(input, constraints = {}) {
    let nep = module.exports.parser()
    let res = best(limit(nep.feed(input).results, constraints))

    if (!res) throw new Error('edtf: No possible parsings (@EOS)')

    return res
  },

  parser() {
    return new nearley.Parser(grammar.ParserRules, grammar.ParserStart)
  }
}

'use strict'

const nearley = require('nearley')
const grammar = require('./grammar')

function byLevel(a, b) {
  return a.level < b.level ? -1 : a.level > b.level ? 1 : 0
}

function limit(results, { level, types }) {
  if (!results.length) return results
  if (typeof level !== 'number') level = 2

  return results.filter(res =>
    (level >= res.level) && (!types || types.includes(res.type)))
}

function best(results) {
  if (results.length < 2) return results[0]

  // If there are multiple results, pick the first
  // one on the lowest level!
  return results.sort(byLevel)[0]
}

module.exports = {

  parse(input, constraints = {}) {
    try {
      let nep = module.exports.parser()
      let res = best(limit(nep.feed(input).results, constraints))

      if (!res) throw new Error('edtf: No possible parsings (@EOS)')

      return res

    } catch (error) {
      error.message += ` for "${input}"`
      throw error
    }
  },

  parser() {
    return new nearley.Parser(grammar.ParserRules, grammar.ParserStart)
  }
}

Add input to error messages


import React from 'react'
import { inject, observer } from 'mobx-react'

import { IMAGE_BASE_URL } from '../../../constants'
import RedButton from '../../Buttons/Red'

@inject('articleImagesStore')
@observer
export default class Row extends React.Component {

  handleDelete() {
    let articleImageID = this.props.image.id
    this.props.articleImagesStore.delete(articleImageID)
  }

  render() {
    let image = this.props.image
    let thumbURL = IMAGE_BASE_URL + image.thumb
    let regularURL = IMAGE_BASE_URL + image.regular

    return (

          { thumbURL }

          { regularURL }

          <RedButton
            title='Delete'
            onClick={ this.handleDelete.bind(this) }
          />

    )
  }
}

Row.propTypes = {
  image: React.PropTypes.object.isRequired
}

import React from 'react'
import { inject, observer } from 'mobx-react'

import { IMAGE_BASE_URL } from '../../../constants'
import RedButton from '../../Buttons/Red'

@inject('articleImagesStore')
@observer
export default class Row extends React.Component {

  handleDelete() {
    let articleImageID = this.props.image.id
    this.props.articleImagesStore.delete(articleImageID)
  }

  render() {
    let image = this.props.image
    let thumbURL = IMAGE_BASE_URL + image.thumb
    let regularURL = IMAGE_BASE_URL + image.regular
    let originalURL = IMAGE_BASE_URL + image.original

    return (

           Thumbnail URL

           Regular URL

           Original URL

          <RedButton
            title='Delete'
            onClick={ this.handleDelete.bind(this) }
          />

    )
  }
}

Row.propTypes = {
  image: React.PropTypes.object.isRequired
}

Add original ArticleImage URL to admin panel


var drFrankenstyle = require('dr-frankenstyle');
var through2 = require('through2');
var File = require('vinyl');
var namespaceAssets = require('dr-frankenstyle/lib/strategies').namespaceAssets;

module.exports = function() {
  var assetsStream = through2.obj();
  drFrankenstyle(function(file, callback) {
    namespaceAssets(file);
    var vinylFile = Object.setPrototypeOf(file, new File(file));
    vinylFile.drFrankenstyle = true;
    assetsStream.write(vinylFile, function(error) {
      if (file.path === 'components.css') {
        assetsStream.end(function() {
          callback(error, file);
        });
      } else {
        callback(error, file);
      }
    });
  });
  return assetsStream;
};

module.exports.done = function() {
  return through2.obj(function(vinylFile, encoding, callback) {
    if (vinylFile.drFrankenstyle) {
      callback(null, Object.getPrototypeOf(vinylFile));
    } else {
      callback(null, vinylFile);
    }
  });
};

var drFrankenstyle = require('dr-frankenstyle');
var through2 = require('through2');
var File = require('vinyl');
var namespaceAssets = require('dr-frankenstyle/dist/strategies').namespaceAssets;

module.exports = function() {
  var assetsStream = through2.obj();
  drFrankenstyle(function(file, callback) {
    namespaceAssets(file);
    var vinylFile = Object.setPrototypeOf(file, new File(file));
    vinylFile.drFrankenstyle = true;
    assetsStream.write(vinylFile, function(error) {
      if (file.path === 'components.css') {
        assetsStream.end(function() {
          callback(error, file);
        });
      } else {
        callback(error, file);
      }
    });
  });
  return assetsStream;
};

module.exports.done = function() {
  return through2.obj(function(vinylFile, encoding, callback) {
    if (vinylFile.drFrankenstyle) {
      callback(null, Object.getPrototypeOf(vinylFile));
    } else {
      callback(null, vinylFile);
    }
  });
};

Fix bad require path in gulp plugin


define('app/views/user_menu', ['app/views/templated', 'md5'],
    /**
     *  User Menu View
     *
     *  @returns Class
     */
    function (TemplatedView) {

        'user strict';

        return TemplatedView.extend({

            /**
             *  Properties
             */

            isNotCore: !IS_CORE,
            accountUrl: URL_PREFIX + '/account',
            //TODO: change the logo_splash.png to user.png
            gravatarURL: EMAIL && ('https://www.gravatar.com/avatar/' + md5(EMAIL) + '?d=' +
                  encodeURIComponent('https://mist.io/resources/images/user.png') +'&s=36'),

            /**
             *
             *  Actions
             *
             */

            actions: {

                meClicked: function(){
                    $('#user-menu-popup').popup('open');
                },

                loginClicked: function() {
                    $('#user-menu-popup').popup('close');
                    Ember.run.later(function() {
                        Mist.loginController.open();
                    }, 300);
                },

                logoutClicked: function() {
                    Mist.loginController.logout();
                }
            }
        });
    }
);

define('app/views/user_menu', ['app/views/templated', 'md5'],
    /**
     *  User Menu View
     *
     *  @returns Class
     */
    function (TemplatedView) {

        'user strict';

        return TemplatedView.extend({

            /**
             *  Properties
             */

            isNotCore: !IS_CORE,
            accountUrl: URL_PREFIX + '/account',
            gravatarURL: EMAIL && ('https://www.gravatar.com/avatar/' + md5(EMAIL) + '?d=' +
                  encodeURIComponent('https://mist.io/resources/images/sprite-images/user.png') +'&s=36'),

            /**
             *
             *  Actions
             *
             */

            actions: {

                meClicked: function(){
                    $('#user-menu-popup').popup('open');
                },

                loginClicked: function() {
                    $('#user-menu-popup').popup('close');
                    Ember.run.later(function() {
                        Mist.loginController.open();
                    }, 300);
                },

                logoutClicked: function() {
                    Mist.loginController.logout();
                }
            }
        });
    }
);

Fix url for default user image


'use strict'

var test = require('tape')
var mito = require('./')

test('basic templating', function (t) {
    t.plan(3)

    t.equal('foo', mito('foo')())
    t.equal('
Foo
', mito('
<%= title %>
')({title: 'Foo'}))
    t.equal('
Baz!
', mito('<p class="<%= cls %>">Baz!
')({cls: 'bar'}))
})

test('for statement', function (t) {
    t.plan(1)

    t.equal('235711', mito('<% items.forEach(function (item) { %><%= item %><% }) %>')({
        items: [2, 3, 5, 7, 11]
    }))
})

test('white spaces, all whitespaces become 0x20', function (t) {
    t.plan(4)

    t.equal(' ', mito('\t')())
    t.equal(' ', mito('\r')())
    t.equal(' ', mito('\n')())
    t.equal(' ', mito(' ')())
})

test('if it has a single quote', function (t) {
    t.plan(1)

    try {
        mito("")
        t.assert(false)
    } catch (e) {
        t.assert(true, 'cannot handle single quote')
    }
})

'use strict'

var test = require('tape')
var mito = require('./')

test('basic templating', function (t) {
    t.plan(3)

    t.equal(mito('foo')(), 'foo')
    t.equal(mito('
<%= title %>
')({title: 'Foo'}), '
Foo
')
    t.equal(mito('<p class="<%= cls %>">Baz!
')({cls: 'bar'}), '
Baz!
')
})

test('for statement', function (t) {
    t.plan(1)

    t.equal(mito('<% items.forEach(function (item) { %><%= item %><% }) %>')({
        items: [2, 3, 5, 7, 11]
    }), '235711')
})

test('white spaces, all whitespaces become 0x20', function (t) {
    t.plan(4)

    t.equal(mito('\t')(), ' ')
    t.equal(mito('\r')(), ' ')
    t.equal(mito('\n')(), ' ')
    t.equal(mito(' ')(), ' ')
})

test('if it has a single quote', function (t) {
    t.plan(1)

    try {
        mito("")()
        t.assert(false)
    } catch (e) {
        t.assert(true, 'cannot handle single quote')
    }
})

Fix the position of expected and actual


module.exports = {
  checkCampaignModel: function (redisClient, accountHashID, campaignHashID, callback) {

  }
}

var configuration = require('../../config/configuration.json')
var utility = require('../../public/method/utility')

module.exports = {
  checkCampaignModel: function (redisClient, accountHashID, payload, callback) {
    var begTime = utility.getUnixTimeStamp() - configuration.MinimumDelay
    var endTime = utility.getUnixTimeStamp() + configuration.MinimumDeuration
    var tableName = configuration.TableMAAccountModelAnnouncerAccountModel + accountHashID
    redisClient.hget(tableName, configuration.ConstantAMAAMBudget, function (err, replies) {
      if (err) {
        callback(err, null)
        return
      }
      if (parseInt(payload[configuration.ConstantCMBudget]) <= parseInt(replies)) {
        // First Check Pass
        if (parseInt(payload[configuration.ConstantCMBeginningTime]) >= begTime) {
          // Second Check Pass
          if (parseInt(payload[configuration.ConstantCMEndingTime]) >= endTime) {
            // Third Check Pass
            callback(null, 'Successful Check')
          }
          else {
            callback(new Error('Ending Time Problem'), null)
            return
          }
        }
        else {
          callback(new Error('Beginning Time Problem'), null)
          return
        }
      }
      else {
        callback(new Error('Budget Problem'), null)
        return
      }
    })
  },

  }
}

Implement checkCampaignModel for Update Purposes


angular.module('app').factory('api', function($http, $q, config){

    return {
        single: function(sexe){

            sexe = sexe || 'mal';

            var deferred = $q.defer();

            $http({
                method: 'GET',
                url: config.apiUrl+'/blind/generate/single/'+sexe
            })
            .success(function(result){
                deferred.resolve(result);
            })
            .error(function(result){
                deferred.resolve('Erreur :'+result);
            });

            return deferred.promise;

        }
    };

});

angular.module('app').factory('api', function ($http, $q, config) {

    return {
        /*
         Generate Single questions (20)
         $param : sexe
         */
        single: function (sexe) {

            sexe = sexe || 'mal';

            var deferred = $q.defer();

            $http({
                method: 'GET',
                url: config.apiUrl + '/blind/generate/single/' + sexe
            })
            .success(function (result) {
                deferred.resolve(result);
            })
            .error(function (result) {
                deferred.resolve('Erreur :' + result);
            });

            return deferred.promise;

        },

        /*
         Generate Number Questions with Category
         @params : category
         @params : number
         */
        multi: function (category, number) {

            category = category || 'geek';
            number = number || 1;

            var deferred = $q.defer();

            $http({
                method: 'GET',
                url: config.apiUrl + '/blind/generate/multi/' + category + '/' + number
            })
            .success(function (result) {
                deferred.resolve(result);
            })
            .error(function (result) {
                deferred.resolve('Erreur :' + result);
            });

            return deferred.promise;

        }
    };

});

Add on ApiService functions for generate multi questions


const DEFAULT_WHITELISTED_URL_REGEXPS = [
  'abcnews.go.com\/.+',
  'arstechnica.com\/.+',
  'bbc.co.uk\/.+',
  'bbc.com\/.+',
  'business-standard.com\/.+',
  'cnn.com\/.+',
  'economist.com\/.+',
  'guardian.co.uk\/.+',
  'theguardian.com\/.+',
  'hollywoodreporter.com\/.+',
  'huffingtonpost.com\/.+',
  'irishtimes.com\/.+',
  'independent.co.uk\/.+',
  'npr.org\/.+',
  'newsweek.com\/.+',
  'nytimes.com\/.+',
  'politico.com\/.+',
  'rollingstone.com\/.+',
  'spiegel.de\/.+',
  'time.com\/.+',
  'theatlantic.com\/.+',
  'variety.com\/.+',
  'washingtonpost.com\/.+',
  'wired.com\/.+',
  'wsj.com\/.+',
];

function saveURLsWhitelist(whitelist) {
  localStorage.setItem('urls-whitelist', JSON.stringify(whitelist));
}

function loadURLsWhitelist() {
  let whitelist = localStorage.getItem('urls-whitelist');
  if (whitelist) {
    return JSON.parse(whitelist);
  }
  return DEFAULT_WHITELISTED_URL_REGEXPS;
}

const DEFAULT_WHITELISTED_URL_REGEXPS = [
  'abcnews.go.com\/.+',
  'arstechnica.com\/.+',
  'bbc.co.uk\/.+',
  'bbc.com\/.+',
  'business-standard.com\/.+',
  'cnn.com\/.+',
  'economist.com\/.+',
  'forbes.com\/.+',
  'guardian.co.uk\/.+',
  'hollywoodreporter.com\/.+',
  'huffingtonpost.com\/.+',
  'independent.co.uk\/.+',
  'irishtimes.com\/.+',
  'newsweek.com\/.+',
  'newyorker.com\/.+',
  'npr.org\/.+',
  'nytimes.com\/.+',
  'politico.com\/.+',
  'rollingstone.com\/.+',
  'spiegel.de\/.+',
  'theatlantic.com\/.+',
  'theguardian.com\/.+',
  'time.com\/.+',
  'variety.com\/.+',
  'washingtonpost.com\/.+',
  'wired.com\/.+',
  'wsj.com\/.+',
];

function saveURLsWhitelist(whitelist) {
  localStorage.setItem('urls-whitelist', JSON.stringify(whitelist));
}

function loadURLsWhitelist() {
  let whitelist = localStorage.getItem('urls-whitelist');
  if (whitelist) {
    return JSON.parse(whitelist);
  }
  return DEFAULT_WHITELISTED_URL_REGEXPS;
}

Add a couple more items to the default whitelist.


/**
 * @module timed
 * @submodule timed-components
 * @public
 */
import Component from 'ember-component'
import service from 'ember-service/inject'
import { observes } from 'ember-computed-decorators'
import { later } from 'ember-runloop'

const ENTER_CHAR_CODE = 13

/**
 * The tracking bar component
 *
 * @class TrackingBarComponent
 * @extends Ember.Component
 * @public
 */
export default Component.extend({
  tracking: service('tracking'),

  /**
   * Key press event
   *
   * Start activity if the pressed key is enter
   *
   * @event keyPress
   * @param {jQuery.Event} e The keypress event
   * @public
   */
  keyPress(e) {
    if (
      e.which === ENTER_CHAR_CODE &&
      !e.target.classList.contains('tt-input')
    ) {
      this.get('tracking.startActivity').perform()
    }
  },

  /**
   * Set the focus to the comment field as soon as the task is selected
   *
   * The 'later' needs to be there so that the focus happens after all the
   * other events are done. Otherwise it'd focus the play button.
   *
   * @method _setCommentFocus
   * @public
   */
  @observes('tracking.activity.task')
  _setCommentFocus() {
    if (this.get('tracking.activity.task.id')) {
      later(this, () => {
        this.$('input[name=comment]').focus()
      })
    }
  }
})

/**
 * @module timed
 * @submodule timed-components
 * @public
 */
import Component from 'ember-component'
import service from 'ember-service/inject'
import { observes } from 'ember-computed-decorators'
import { later } from 'ember-runloop'

const ENTER_CHAR_CODE = 13

/**
 * The tracking bar component
 *
 * @class TrackingBarComponent
 * @extends Ember.Component
 * @public
 */
export default Component.extend({
  tracking: service('tracking'),

  /**
   * Key press event
   *
   * Start activity if the pressed key is enter
   *
   * @event keyPress
   * @param {jQuery.Event} e The keypress event
   * @public
   */
  keyPress(e) {
    if (
      e.which === ENTER_CHAR_CODE &&
      !e.target.classList.contains('tt-input')
    ) {
      this.get('tracking.startActivity').perform()
    }
  },

  /**
   * Set the focus to the comment field as soon as the task is selected
   *
   * The 'later' needs to be there so that the focus happens after all the
   * other events are done. Otherwise it'd focus the play button.
   *
   * @method _setCommentFocus
   * @public
   */
  @observes('tracking.activity.task')
  _setCommentFocus() {
    later(this, () => {
      if (this.get('tracking.activity.task.id')) {
        this.$('input[name=comment]').focus()
      }
    })
  }
})

Fix focus jumping to comment after cleaning the customer


var slice = [].slice

function Vestibule () {
    this._waiting = []
    this.waiting = 0
    this.open = null
}

Vestibule.prototype.enter = function (callback) {
    if (this.open == null) {
        var cookie = {}
        this.waiting++
        this._waiting.push({
            cookie: cookie,
            callback: callback
        })
        return cookie
    }
    callback.apply(null, this.open)
    return null
}

Vestibule.prototype.leave = function (cookie) {
    for (var i = 0, I = this._waiting.length; i < I; i++) {
        if (this._waiting[i].cookie === cookie) {
            this._waiting.splice(i, 1)
            this.waiting--
            break
        }
    }
}

Vestibule.prototype.notify = function () {
    var vargs = slice.call(arguments)
    this.waiting = 0
    this._waiting.splice(0, this._waiting.length).forEach(function (waiting) {
        waiting.callback.apply(null, vargs)
    })
}

module.exports = Vestibule

var slice = [].slice

function Vestibule () {
    this._waiting = []
    this.occupied = false
    this.open = null
}

Vestibule.prototype.enter = function (callback) {
    if (this.open == null) {
        var cookie = {}
        this.occupied = true
        this._waiting.push({
            cookie: cookie,
            callback: callback
        })
        return cookie
    }
    callback.apply(null, this.open)
    return null
}

Vestibule.prototype.leave = function (cookie) {
    for (var i = 0, I = this._waiting.length; i < I; i++) {
        if (this._waiting[i].cookie === cookie) {
            this._waiting.splice(i, 1)
            this.waiting--
            break
        }
    }
}

Vestibule.prototype.notify = function () {
    var vargs = slice.call(arguments)
    this.occupied = false
    this._waiting.splice(0, this._waiting.length).forEach(function (waiting) {
        waiting.callback.apply(null, vargs)
    })
}

module.exports = Vestibule

Use `occupied` instead of `waiting`.


import React, { Component } from 'react';
import { getProjectViewUrl } from 'utils/urlUtil';
import marked from 'marked';

import styles from './ProjectPreview.styl';

class ProjectPreview extends Component {

  render () {

    const { project, params } = this.props;

    if (!project) return
;

    const viewUrl = getProjectViewUrl(params) + `?source=${encodeURI(project.source)}`;

    return (

    )
  }
}

export default ProjectPreview;

import React from 'react';
import { getProjectViewUrl } from 'utils/urlUtil';

import styles from './ProjectPreview.styl';

export default ({ project, params }) => {
  if (!project) return
;

  return (

        <iframe
          src={`${getProjectViewUrl(params)}?source=${encodeURI(project.source)}`}>

  )
}

Convert to a functional stateless component


require('babel-register')({
  // ignore node_modules except node_modules/react-native-gifted-spinner and node_modules/react-native-lock
  // because they need to be transpiled
  // syntax: /node_modules\/(?!(library1|library2))/
  ignore: /node_modules\/(?!(react-native-gifted-spinner|react-native-lock))/
});

global.__DEV__ = true;

require('babel-register')({
  // ignore node_modules except node_modules/react-native-lock,
  // because it needs to be transpiled
  // syntax: /node_modules\/(?!(library1|library2))/
  ignore: /node_modules\/(?!(react-native-lock))/
});

Define __DEV__ flag for tests


import { createStore, applyMiddleware, compose } from 'redux';
import { autoRehydrate, persistStore } from 'redux-persist';
import persist from 'remotedev-app/lib/middlewares/persist';
import reduxAPI from '../middlewares/redux-api';
import reducer from '../reducers';

const middlewares = applyMiddleware(reduxAPI, persist());

// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
/* eslint-enable no-underscore-dangle */
const enhancer = composeEnhancers(
  middlewares,
  autoRehydrate()
);

export default initialState => {
  const store = createStore(reducer, initialState, enhancer);
  persistStore(store, { whitelist: ['setting'] });
  return store;
};

import { createStore, applyMiddleware, compose } from 'redux';
import { autoRehydrate, persistStore } from 'redux-persist';
import reduxAPI from '../middlewares/redux-api';
import reducer from '../reducers';

const middlewares = applyMiddleware(reduxAPI);

// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
/* eslint-enable no-underscore-dangle */
const enhancer = composeEnhancers(
  middlewares,
  autoRehydrate()
);

export default initialState => {
  const store = createStore(reducer, initialState, enhancer);
  persistStore(store, { whitelist: ['setting', 'monitor', 'test'] });
  return store;
};

Remove persist middleware of remotedev-app & Add monitor, test to persist whitelist


'use strict';

// MODULES //

var specificationClass = require( '@stdlib/utils/specification-class' );
var RE = require( '@stdlib/regex/function-name' );
var isBuffer = require( '@stdlib/utils/is-buffer' );

// CONSTRUCTOR NAME //

/**
* FUNCTION: constructorName( v )
*	Determines the name of a value's constructor.
*
* @param {*} v - input value
* @returns {String} name of a value's constructor
*/
function constructorName( v ) {
	var name;
	var ctor;
	name = specificationClass( v ).slice( 8, -1 );
	if ( (name === 'Object' || name === 'Error') && v.constructor ) {
		ctor = v.constructor;
		if ( typeof ctor.name === 'string' ) {
			return ctor.name;
		}
		return RE.exec( ctor.toString() )[ 1 ];
	}
	if ( isBuffer( v ) ) {
		return 'Buffer';
	}
	return name;
} // end FUNCTION constructorName()

// EXPORTS //

module.exports = constructorName;

'use strict';

// MODULES //

var nativeClass = require( '@stdlib/utils/native-class' );
var RE = require( '@stdlib/regex/function-name' );
var isBuffer = require( '@stdlib/utils/is-buffer' );

// CONSTRUCTOR NAME //

/**
* FUNCTION: constructorName( v )
*	Determines the name of a value's constructor.
*
* @param {*} v - input value
* @returns {String} name of a value's constructor
*/
function constructorName( v ) {
	var name;
	var ctor;
	name = nativeClass( v ).slice( 8, -1 );
	if ( (name === 'Object' || name === 'Error') && v.constructor ) {
		ctor = v.constructor;
		if ( typeof ctor.name === 'string' ) {
			return ctor.name;
		}
		return RE.exec( ctor.toString() )[ 1 ];
	}
	if ( isBuffer( v ) ) {
		return 'Buffer';
	}
	return name;
} // end FUNCTION constructorName()

// EXPORTS //

module.exports = constructorName;

Update fcn to get an object's native class


'use strict';

// MODULES //

var isString = require( '@stlib/utils/is-string' )[ 'primitive' ];

// BINARY STRING //

/**
* FUNCTION: isBinaryString( value )
*	Tests if a value is a binary string.
*
* @param {*} value - value to test
* @returns {Boolean} boolean indicating if an input value is a binary string
*/
function isBinaryString( str ) {
	var len;
	var ch;
	var i;
	if ( !isString( str ) ) {
		return false;
	}
	len = str.length;
	if ( !len ) {
		return false;
	}
	for ( i = 0; i < len; i++ ) {
		ch = str[ i ];
		if ( ch !== '1' && ch !== '0' ) {
			return false;
		}
	}
	return true;
} // end FUNCTION isBinaryString()

// EXPORTS //

module.exports = isBinaryString;

'use strict';

// MODULES //

var isString = require( '@stdlib/utils/is-string' )[ 'primitive' ];

// BINARY STRING //

/**
* FUNCTION: isBinaryString( value )
*	Tests if a value is a binary string.
*
* @param {*} value - value to test
* @returns {Boolean} boolean indicating if an input value is a binary string
*/
function isBinaryString( str ) {
	var len;
	var ch;
	var i;
	if ( !isString( str ) ) {
		return false;
	}
	len = str.length;
	if ( !len ) {
		return false;
	}
	for ( i = 0; i < len; i++ ) {
		ch = str[ i ];
		if ( ch !== '1' && ch !== '0' ) {
			return false;
		}
	}
	return true;
} // end FUNCTION isBinaryString()

// EXPORTS //

module.exports = isBinaryString;

Fix typo in require statement of is-binary-string


// ==========================================================================
// Application JavaScript for IMTD?
// ==========================================================================

document.addEventListener('DOMContentLoaded', init);

function init() {
	var trainInfo = document.querySelector('.train-info');

	trainInfo.addEventListener('click', function(e) {
		handleDelayInfoClick(e);
	});

	function handleDelayInfoClick(event) {
		event.preventDefault();

		if (event.target.parentElement.parentElement.nextSibling.classList.contains('delay-info--show')) {
			event.target.parentElement.parentElement.nextSibling.classList.remove('delay-info--show')
		}
		else {
			event.target.parentElement.parentElement.nextSibling.classList.add('delay-info--show');
		}
	}
	// function handleRefresh() {
	// 	var oReq = new XMLHttpRequest();
	// 	oReq.addEventListener("load", handleResponse);
	// 	oReq.open("GET", "http://localhost:5000/fetch");
	// 	oReq.send();
	// }
	// function handleResponse(res) {
	// 	console.log(res);
	// 	var data = JSON.parse(res.target.response);
	// 	if (res.target.status === 200) {
	// 		// OK

	// 	}
	}
}

// ==========================================================================
// Application JavaScript for IMTD?
// ==========================================================================

document.addEventListener('DOMContentLoaded', init);

function init() {
	var trainInfo = document.querySelector('.train-info');

	trainInfo.addEventListener('click', function(e) {
		handleDelayInfoClick(e);
	});

	function handleDelayInfoClick(event) {
		event.preventDefault();

		if (event.target.parentElement.parentElement.nextSibling.classList.contains('delay-info--show')) {
			event.target.parentElement.parentElement.nextSibling.classList.remove('delay-info--show')
		}
		else {
			event.target.parentElement.parentElement.nextSibling.classList.add('delay-info--show');
		}
	}
	// function handleRefresh() {
	// 	var oReq = new XMLHttpRequest();
	// 	oReq.addEventListener("load", handleResponse);
	// 	oReq.open("GET", "http://localhost:5000/fetch");
	// 	oReq.send();
	// }
	// function handleResponse(res) {
	// 	console.log(res);
	// 	var data = JSON.parse(res.target.response);
	// 	if (res.target.status === 200) {
	// 		// OK

	// 	}
	//}
}

Comment out missed curly brace


exports.up = (knex, Promise) => {
  return Promise.all([
    knex.schema.raw('ALTER TABLE transactions ALTER COLUMN new_saldo decimal(8,2) not null;'),
    knex.schema.raw('ALTER TABLE transactions ALTER COLUMN old_saldo decimal(8,2) not null;'),
    knex.schema.raw('ALTER TABLE user_saldos ALTER COLUMN saldo decimal(8,2) not null;'),
  ]);
};

exports.down = (knex, Promise) => {
  return Promise.all([
    knex.schema.raw('ALTER TABLE transactions ALTER COLUMN new_saldo decimal(18,0) not null;'),
    knex.schema.raw('ALTER TABLE transactions ALTER COLUMN old_saldo decimal(18,0) not null;'),
    knex.schema.raw('ALTER TABLE user_saldos ALTER COLUMN saldo decimal(18,0) not null;'),
  ]);
};

exports.up = (knex, Promise) => {
  return Promise.all([
    knex.schema.raw('ALTER TABLE transactions ALTER COLUMN new_saldo decimal(8,2) not null;'),
    knex.schema.raw('ALTER TABLE transactions ALTER COLUMN old_saldo decimal(8,2) not null;'),
    knex.schema.raw('ALTER TABLE user_saldos ALTER COLUMN saldo decimal(8,2) not null;'),
  ]);
};

exports.down = (knex, Promise) => Promise.resolve();

Discard rollback commands because we don't want get back to decimal(18,0) datatype


const GitBook = require('gitbook-core');
const { React } = GitBook;

const Page = require('./Page');
const Toolbar = require('./Toolbar');

const Body = React.createClass({
    propTypes: {
        page:   GitBook.PropTypes.Page,
        readme: GitBook.PropTypes.Readme
    },

    render() {
        const { page, readme } = this.props;

        return (
            <div className="Body page-wrapper">
                <Toolbar title={page.title} readme={readme} />
                <Page page={page} />
            </div>
        );
    }
});

module.exports = Body;

const GitBook = require('gitbook-core');
const { React } = GitBook;

const Page = require('./Page');
const Toolbar = require('./Toolbar');

const Body = React.createClass({
    propTypes: {
        page:   GitBook.PropTypes.Page,
        readme: GitBook.PropTypes.Readme
    },

    render() {
        const { page, readme } = this.props;

        return (
            <GitBook.InjectedComponent matching={{ role: 'body:wrapper' }}>
                <div className="Body page-wrapper">
                    <GitBook.InjectedComponent matching={{ role: 'toolbar:wrapper' }}>
                        <Toolbar title={page.title} readme={readme} />
                    </GitBook.InjectedComponent>
                    <GitBook.InjectedComponent matching={{ role: 'page:wrapper' }}>
                        <Page page={page} />
                    </GitBook.InjectedComponent>
                </div>
            </GitBook.InjectedComponent>
        );
    }
});

module.exports = Body;

Add roles "body:wrapper", "toolbar:wrapper", "page:wrapper"


import createAuthenticator from '../createAuthenticator'
import identity from '../utils/identity'
import invariant from 'invariant'

export default ({
  endpoint,
  contentType = 'application/json',
  headers = {},
  method = 'POST',
  transformRequest = JSON.stringify,
  transformResponse = identity
}) => {
  invariant(
    endpoint,
    'You must provide an endpoint for the `credentials` authenticator'
  )

  return createAuthenticator({
    name: 'credentials',
    authenticate: credentials =>
      fetch(endpoint, {
        method,
        headers: {
          'Content-Type': contentType,
          ...headers
        },
        body: transformRequest(credentials)
      }).then(async response => {
        const json = await response.json()

        if (!response.ok) {
          return Promise.reject(json)
        }

        return transformResponse(json)
      }),
    restore: data => {
      if (Object.keys(data).length > 0) {
        return Promise.resolve(data)
      }

      return Promise.reject()
    }
  })
}

import createAuthenticator from '../createAuthenticator'
import identity from '../utils/identity'
import invariant from 'invariant'

const defaultRestore = data => {
  if (Object.keys(data).length > 0) {
    return Promise.resolve(data)
  }

  return Promise.reject()
}

export default ({
  endpoint,
  contentType = 'application/json',
  headers = {},
  method = 'POST',
  restore = defaultRestore,
  transformRequest = JSON.stringify,
  transformResponse = identity
}) => {
  invariant(
    endpoint,
    'You must provide an endpoint for the `credentials` authenticator'
  )

  return createAuthenticator({
    name: 'credentials',
    authenticate: credentials =>
      fetch(endpoint, {
        method,
        headers: {
          'Content-Type': contentType,
          ...headers
        },
        body: transformRequest(credentials)
      }).then(async response => {
        const json = await response.json()

        if (!response.ok) {
          return Promise.reject(json)
        }

        return transformResponse(json)
      }),
    restore
  })
}

Allow restore to be overridden


import Ember from 'ember';
import Application from '../../app';
import Router from '../../router';
import config from '../../config/environment';

export default function startApp(attrs) {
  var application;

  var attributes = Ember.merge({}, config.APP);
  attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;

  Ember.run(function() {
    application = Application.create(attributes);
    application.setupForTesting();
    application.injectTestHelpers();
  });

  return application;
}

import Ember from 'ember';
import Application from '../../app';
import Router from '../../router';
import config from '../../config/environment';

export default function startApp(attrs) {
  var application;

  let attributes = Ember.assign({}, config.APP, attrs); // use defaults, but you can override;

  Ember.run(function() {
    application = Application.create(attributes);
    application.setupForTesting();
    application.injectTestHelpers();
  });

  return application;
}

Remove Ember.merge deprecation warnings. Dummy app requires Ember.assign.


(function () {
	'use strict';

	mare.views.SocialWorkerRegistration = Backbone.View.extend({
		el: '.registration-form--social-worker',

		initialize: function() {
			// Initialize parsley validation on the form
			this.form = this.$el.parsley();
			this.form.on('field:validated', this.validateForm);
		},

		validateForm: function validateForm() {
			var ok = $('.parsley-error').length === 0;
			$('.bs-callout-info').toggleClass('hidden', !ok);
			$('.bs-callout-warning').toggleClass('hidden', ok);
		}

	});
})();

(function () {
	'use strict';

	mare.views.SocialWorkerRegistration = Backbone.View.extend({
		el: '.registration-form--social-worker',

		events: {
			'change .social-worker-title-checkbox': 'toggleSocialWorkerTitleTextField'
		},

		initialize: function() {
			// DOM cache any commonly used elements to improve performance
			this.$socialWorkerTitle = this.$('#social-worker-title');
			this.$socialWorkerTitleGroup = this.$('.social-worker-title-group');
			// Initialize parsley validation on the form
			this.form = this.$el.parsley();
			// Bind the hidden 'title' text box for use in binding/unbinding validation
			this.socialWorkerTitleValidator = this.$socialWorkerTitle.parsley();
			// DOM cache the Parsley validation message for the hidden 'other' field for use in binding/unbinding validation
			this.$socialWorkerTitleErrorMessage = this.$socialWorkerTitle.next();

			this.form.on('field:validated', this.validateForm);
		},

		toggleSocialWorkerTitleTextField: function toggleSocialWorkerTitleTextField() {
			// Hide/show the hidden 'other' field via the hidden class
			this.$socialWorkerTitleGroup.toggleClass('hidden');

			if(this.$socialWorkerTitleGroup.hasClass('hidden')) {
				// Clear out the input box since it's hidden and not part of the form submission
				this.$socialWorkerTitle.val('');
				// Remove the validation binding
				this.$socialWorkerTitle.attr('data-parsley-required', 'false');
				// Reset validation on the field.  If it was already validated, we need to clear out the check so the form can be submitted
				this.socialWorkerTitleValidator.reset();
			} else {
				// Add validation binding
				this.$socialWorkerTitle.attr('data-parsley-required', 'true');
				// Create a custom error message to display if validation fails
				this.$socialWorkerTitle.attr('data-parsley-error-message', 'required');
			}
		},

		validateForm: function validateForm() {
			var ok = $('.parsley-error').length === 0;
			$('.bs-callout-info').toggleClass('hidden', !ok);
			$('.bs-callout-warning').toggleClass('hidden', ok);
		}

	});
})();

Add data validation to custom title field in the social worker registration form.


import React, { Component } from 'react';
import PropTypes from 'prop-types';
import idgen from './idgen';
import Icon from './Icon';
import cx from 'classnames';

class Textarea extends Component {
  componentDidUpdate() {
    M.textareaAutoResize(this._textarea);
  }

  renderIcon(icon) {
    return <Icon className="material-icons prefix">{icon}</Icon>;
  }

  render() {
    const {
      className,
      iconClassName,
      icon,
      label,
      id,
      onChange,
      ...other
    } = this.props;

    const computedId = id || idgen();

    return (

        {icon && this.renderIcon(icon, iconClassName)}
        <textarea
          ref={input => {
            this._textarea = input;
          }}
          onChange={onChange}
          id={computedId}
          className={cx('materialize-textarea', className)}
          {...other}
        />
        {label}

    );
  }
}

Textarea.propTypes = {
  /*
   * render icon next to input
   */
  icon: PropTypes.string,
  /*
   * icon classname
   */
  iconClassName: PropTypes.string,
  /*
   * textarea label
   */
  label: PropTypes.string,
  /*
   * onChange callback
   */
  onChange: PropTypes.func,
  /*
   * override id
   * @default idgen()
   */
  id: PropTypes.string,
  /*
   * disabled input
   */
  disabled: PropTypes.bool,
  /*
  * predefined value
  */
  value: PropTypes.string,
  className: PropTypes.string
};

export default Textarea;

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import idgen from './idgen';
import Icon from './Icon';
import cx from 'classnames';

class Textarea extends Component {
  componentDidUpdate() {
    M.textareaAutoResize(this._textarea);
  }

  renderIcon(icon) {
    return <Icon className="material-icons prefix">{icon}</Icon>;
  }

  render() {
    const {
      className,
      iconClassName,
      icon,
      label,
      id,
      onChange,
      ...other
    } = this.props;

    const computedId = id || idgen();

    return (

        {icon && this.renderIcon(icon, iconClassName)}
        <textarea
          {...other}
          ref={input => {
            this._textarea = input;
          }}
          onChange={onChange}
          id={computedId}
          className={cx('materialize-textarea', className)}
        />
        {label}

    );
  }
}

Textarea.propTypes = {
  /*
   * render icon next to input
   */
  icon: PropTypes.string,
  /*
   * icon classname
   */
  iconClassName: PropTypes.string,
  /*
   * textarea label
   */
  label: PropTypes.string,
  /*
   * onChange callback
   */
  onChange: PropTypes.func,
  /*
   * override id
   * @default idgen()
   */
  id: PropTypes.string,
  /*
   * disabled input
   */
  disabled: PropTypes.bool,
  /*
  * predefined value
  */
  value: PropTypes.string,
  className: PropTypes.string
};

export default Textarea;

Change TextArea overwrite props order


/**
 * @copyright Copyright (c) 2014 2amigOS! Consulting Group LLC
 * @link http://2amigos.us
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 */
if (typeof dosamigos == "undefined" || !dosamigos) {
    var dosamigos = {};
}
dosamigos.toggleColumn = (function ($) {
    var pub = {
        onText: 'On',
        offText: 'Off',
        onTitle: 'On',
        offTitle: 'Off',
        registerHandler: function (grid, selector, cb) {
            $(document).on('click.toggleColumn', selector, function (e) {
                e.preventDefault();
                var $self = $(this);
                var url = $self.attr('href');
                $.ajax({
                    url: url,
                    dataType: 'json',
                    success: function (data) {
                        $self
                            .html(data.value ? pub.onText : pub.offText)
                            .attr('title', data.value ? pub.onTitle : pub.offTitle);
                        $.isFunction(cb) && cb(true, data);
                    },
                    error: function (xhr) {
                        $.isFunction(cb) && cb(false, xhr);
                    }
                });
                return false;
            });
        }
    };
    return pub;
})(jQuery);

/**
 * @copyright Copyright (c) 2014 2amigOS! Consulting Group LLC
 * @link http://2amigos.us
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 */
if (typeof dosamigos == "undefined" || !dosamigos) {
    var dosamigos = {};
}
dosamigos.toggleColumn = (function ($) {
    var pub = {
        onText: 'On',
        offText: 'Off',
        onTitle: 'On',
        offTitle: 'Off',
        registerHandler: function (grid, selector, cb) {
            $(document).off('click.toggleColumn', selector,);
            $(document).on('click.toggleColumn', selector, function (e) {
                e.preventDefault();
                var $self = $(this);
                var url = $self.attr('href');
                $.ajax({
                    url: url,
                    dataType: 'json',
                    success: function (data) {
                        $self
                            .html(data.value ? pub.onText : pub.offText)
                            .attr('title', data.value ? pub.onTitle : pub.offTitle);
                        $.isFunction(cb) && cb(true, data);
                    },
                    error: function (xhr) {
                        $.isFunction(cb) && cb(false, xhr);
                    }
                });
                return false;
            });
        }
    };
    return pub;
})(jQuery);

Fix for duplicated event triggered when using ajax.


const base = require('skatejs-build/karma.conf');
module.exports = function (config) {
  base(config);
  config.files.push('node_modules/webcomponents.js/MutationObserver.js');
};

const base = require('skatejs-build/karma.conf');
module.exports = function (config) {
  base(config);
  config.files = [
    'node_modules/skatejs-named-slots/dist/index.js',
    'node_modules/webcomponents.js/webcomponents.js'
  ].concat(config.files);
};

Include all of webcomponentsjs because it only checks for browser support there instead of in the individual files. Included named slots so we can integration test everything together.


// App config the for production environment.
// Do not require this directly. Use ./src/config instead.

const HOST = process.env.HOST || "api.openfisca.fr",
  apiBaseUrl = process.env.API_URL || `https://${HOST}`,
  gitHubProject = "openfisca/openfisca-france",
  gitWebpageUrl = "https://github.com/openfisca/legislation-explorer",
  piwikConfig = {
    url: "stats.data.gouv.fr",
    siteId: 4,
    trackErrors: true
  },
  useCommitReferenceFromApi = true,
  websiteUrl = "https://www.openfisca.fr/"

export default {
  apiBaseUrl,
  gitHubProject,
  gitWebpageUrl,
  piwikConfig,
  useCommitReferenceFromApi,
  websiteUrl
}

// App config the for production environment.
// Do not require this directly. Use ./src/config instead.

const HOST = process.env.HOST || "api.openfisca.fr",
  apiBaseUrl = process.env.API_URL || `https://${HOST}`,
  gitHubProject = "openfisca/openfisca-france",
  gitWebpageUrl = "https://github.com/openfisca/legislation-explorer",
  piwikConfig = {
    url: "https://stats.data.gouv.fr",
    siteId: 4,
    trackErrors: true
  },
  useCommitReferenceFromApi = true,
  websiteUrl = "https://www.openfisca.fr/"

export default {
  apiBaseUrl,
  gitHubProject,
  gitWebpageUrl,
  piwikConfig,
  useCommitReferenceFromApi,
  websiteUrl
}

Add explicit https url in piwik config


if (!localStorage['saved']){
  localStorage['letA'] = '#800000'; //maroon
  localStorage['letE'] = '#008000'; //green
  localStorage['letI'] = '#0000ff'; //blue
  localStorage['letO'] = '#008080'; //teal
  localStorage['letU'] = '#800080'; //purple

  localStorage['saved'] = 'Y';

}

// Converts an integer (unicode value) to a char
function itoa(i)
{
   return String.fromCharCode(i);
}

// Converts a char into to an integer (unicode value)
function atoi(a)
{
   return a.charCodeAt();
}

chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
	if (request.method == "getColors"){
		var colorList = new Array();
		//first the letters
		for( x=0; x<26; x++){
			colorList[x] = localStorage['let' + itoa( atoi('A') + x) ];
		}
		//now the numbers...
		for( x=0; x<10; x++){
			colorList[x] = localStorage['let' + itoa( atoi('0') + x) ];
		}
		sendResponse({colors: colorList});
	  }
    else{
		sendResponse({}); // snub them.
	  }
});

if (!localStorage['saved']){
  localStorage['letA'] = '#800000'; //maroon
  localStorage['letE'] = '#008000'; //green
  localStorage['letI'] = '#0000ff'; //blue
  localStorage['letO'] = '#008080'; //teal
  localStorage['letU'] = '#800080'; //purple

  localStorage['saved'] = 'Y';

}

// Converts an integer (unicode value) to a char
function itoa(i)
{
   return String.fromCharCode(i);
}

// Converts a char into to an integer (unicode value)
function atoi(a)
{
   return a.charCodeAt();
}

chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
	if (request.method == "getColors"){
		var colorList = new Array();
		//first the letters
		for( x=0; x<26; x++){
			colorList[x] = localStorage['let' + itoa( atoi('A') + x) ];
		}
		//now the numbers...
		for( x=26; x<36; x++){
			colorList[x] = localStorage['let' + itoa( atoi('0') + x - 26) ];
		}
		sendResponse({colors: colorList});
	  }
    else{
		sendResponse({}); // snub them.
	  }
});

Fix bad indexing in array


module.exports = function(model) {
    'use strict';

    let validateProjectAccess = require('./project-access')(model.access);
    let schema = require('.././schema')(model);
    let router = require('koa-router')();
    let projects = require('./projects')(model.projects);
    let samples = require('./samples')(model.samples, schema);
    let processes = require('./processes')(model.processes, schema);

    router.get('/projects2', projects.all);
    router.post('/projects2/:project_id/processes', validateProjectAccess, processes.create);
    router.post('/projects2/:project_id/samples', validateProjectAccess, samples.create);
    router.put('/projects2/:project_id/samples/:sample_id', validateProjectAccess, samples.update);
    router.get('/projects2/:project_id/dir/:directory_id', validateProjectAccess, projects.dirTree);

    return router;
};

module.exports = function(model) {
    'use strict';

    let validateProjectAccess = require('./project-access')(model.access);
    let schema = require('.././schema')(model);
    let router = require('koa-router')();
    let projects = require('./projects')(model.projects);
    let samples = require('./samples')(model.samples, schema);
    let files = require('./files')(model.files);
    let processes = require('./processes')(model.processes, schema);

    router.get('/projects2', projects.all);
    router.get('/projects2/:project_id/dir/:directory_id', validateProjectAccess, projects.dirTree);

    router.post('/projects2/:project_id/processes', validateProjectAccess, processes.create);

    router.post('/projects2/:project_id/samples', validateProjectAccess, samples.create);
    router.put('/projects2/:project_id/samples/:sample_id', validateProjectAccess, samples.update);

    router.get('/projects2/:project_id/files/:file_id', validateProjectAccess, files.get);

    return router;
};

Add REST call for files.


var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:8080/my_database');

var Schema = mongoose.Schema;

// create a schema
var gameInfoSchema = new Schema({
  title: { type: String, required: true },
  descrption: { type: String },
  pictures: [ { type: String } ],
  videos: [ { type: String } ],
  link_facebook: { type: String },
  link_twitter: { type: String },
  link_google_plus: { type: String },
  downloand: { type: String },
  version: { type: String },
  username: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  admin: Boolean,
  location: String,
  created_at: Date,
  updated_at: Date
});

// the schema is useless so far
// we need to create a model using it
var gameInfo = mongoose.model('gameInfo', gameInfoSchema);

// make this available to our users in our Node applications
module.exports = gameInfo;

var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:8080/my_database');

var Schema = mongoose.Schema;

// create a schema
var gameInfoSchema = new Schema({
  title: { type: String, required: true },
  descrption: { type: String },
  pictures: [ { type: String } ],
  videos: [ { type: String } ],
  link_facebook: { type: String },
  link_twitter: { type: String },
  link_google_plus: { type: String },
  download: { type: String },
  version: { type: String },
  username: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  admin: Boolean,
  location: String,
  created_at: Date,
  updated_at: Date
});

// the schema is useless so far
// we need to create a model using it
var gameInfo = mongoose.model('gameInfo', gameInfoSchema);

// make this available to our users in our Node applications
module.exports = gameInfo;

Correct spelling of downloand to download.


(function($) {
  $.fn.sameHeight = function() {
    var these = this;
    function setHeight() {
      var max = 0;
      these.height('auto').each(function() {
        max = Math.max(max, $(this).height());
      }).height(max);
    };
    $(window).resize(setHeight);
    setHeight();
    return this;
  };
})(jQuery);

/*
* sameHeight jQuery plugin
* http://github.com/sidewaysmilk/jquery-sameheight
*
* Copyright 2011, Justin Force
* Licensed under the MIT license
*/
jQuery.fn.sameHeight = function() {
  var these = this;
  function setHeight() {
    var max = 0;
    these.height('auto').each(function() {
      max = Math.max(max, jQuery(this).height());
    }).height(max);
  };
  jQuery(window).resize(setHeight);
  setHeight();
  return this;
};

Add header and don't use $ for jQuery


var PhotoRenderer = function(container, photo) {
  bindEvents();

  this.render = function() {
    startProgress()
    renderImage(photo)

    return this
  }

  function bindEvents() {
    $("a").click(renderNewImage)

    $(window).on("popstate", renderImageFromHistory)
  }

  function renderImage(photo) {
    var dfd = $.Deferred()

    $("").load(function() {
      $(this).remove()

      container
        .css("background-image", "url(" + photo.url + ")")
        .find(".caption").html(photo.photoset.title._content + "
" + photo.title + "
" + photo.ownername).end()
        .fadeIn()

      NProgress.done()
      dfd.resolve()
    }).attr("src", photo.url)

    return dfd
  }

  function renderNewImage(ev) {
    ev.preventDefault()

    startProgress()

    var photoJsonUrl = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + $("a").attr("href") + ".json"
    $.getJSON(photoJsonUrl).done(replaceCurrentImage)
  }

  function renderImageFromHistory() {
    if (!event.state) return

    startProgress()
    renderImage(event.state)
  }

  function replaceCurrentImage(photo) {
    renderImage(photo).done(function() {
      history.pushState(photo, "", photo.browser_url)
    })
  }

  function startProgress() {
    NProgress.configure({minimum: 0.3}).start()
  }
}

var PhotoRenderer = function(container, photo) {
  bindEvents();

  this.render = function() {
    startProgress()
    renderImage(photo)

    return this
  }

  function bindEvents() {
    $("a").click(renderNewImage)

    $(window).on("popstate", renderImageFromHistory)
  }

  function renderImage(photo) {
    var dfd = $.Deferred()

    $("").load(function() {
      $(this).remove()

      container.fadeOut(function() {
        $(this)
          .css("background-image", "url(" + photo.url + ")")
          .find(".caption").html(photo.photoset.title._content + "
" + photo.title + "
" + photo.ownername).end()
          .fadeIn()
      })

      NProgress.done()
      dfd.resolve()
    }).attr("src", photo.url)

    return dfd
  }

  function renderNewImage(ev) {
    ev.preventDefault()

    startProgress()

    var photoJsonUrl = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + $("a").attr("href") + ".json"
    $.getJSON(photoJsonUrl).done(replaceCurrentImage)
  }

  function renderImageFromHistory() {
    if (!event.state) return

    startProgress()
    renderImage(event.state)
  }

  function replaceCurrentImage(photo) {
    renderImage(photo).done(function() {
      history.pushState(photo, "", photo.browser_url)
    })
  }

  function startProgress() {
    NProgress.configure({minimum: 0.3}).start()
  }
}

Add transition between changing images.


michishiki.api = {};
michishiki.api.uri = 'http://amateras.wsd.kutc.kansai-u.ac.jp/~otsuka/michishiki_api_server/';

michishiki.api.getPost = function(options) {
    var dfd = $.Deferred();

    var api_uri = michishiki.api.uri + 'get_post.py';
    var query_string = michishiki.api.utils.optionsToQueryString(options);
    $.getJSON(api_uri + query_string, function(json) {
	dfd.resolve(json);
    });

    return dfd.promise();
};

michishiki.api.post = function(options) {
    var api_url = michishiki.api.uri + 'post.py';

    //
};

michishiki.api.utils = {};

michishiki.api.utils.optionsToQueryString = function(options) {
    var querys = [];
    for (var key in options) {
	var q = key + '=' + options[key];
	querys.push(q);
    }
    return '?' + querys.join('&');
};

michishiki.api = {};
michishiki.api.uri = 'http://amateras.wsd.kutc.kansai-u.ac.jp/~otsuka/michishiki_api_server/';

michishiki.api.getPost = function(options) {
    var dfd = $.Deferred();

    var api_uri = michishiki.api.uri + 'get_post.py';
    var query_string = michishiki.api.utils.optionsToQueryString(options);
    $.getJSON(api_uri + query_string, function(json) {
	dfd.resolve(json);
    });

    return dfd.promise();
};

michishiki.api.post = function(options) {
    var dfd = $.Deferred();

    var api_url = michishiki.api.uri + 'post.py';
    var query_string = michishiki.api.utils.optionsToQueryString(options);
    $.getJSON(api_uri + query_string, function(json) {
	dfd.resolve(json);
    });

    return dfd.promise();
};

michishiki.api.utils = {};

michishiki.api.utils.optionsToQueryString = function(options) {
    var querys = [];
    for (var key in options) {
	var q = key + '=' + options[key];
	querys.push(q);
    }
    return '?' + querys.join('&');
};

Implement function to post by using michishiki API


'use strict';

var _ = require('lodash');

/**
* Format where expression.
*
* @param {string[]|string} keys Array of keys to omit in query
* @param {string[]|string} query Array of parameter to use in where
* @param {string[]|string} jsonkeys Array of keys json to use in query
*/

module.exports = function (keys, query, jsonkeys) {
  var where = _.omit(query, keys);

  /**
   * Omit all keys in 'where' who not value jonskeys[] + '.'
   * Exemple:
   *   where = {id :5, data.team: 456}
   *   jsonkeys = [data]
   * Result where = {data.team: 456} why data.team contains data.
  */
  where = _.reduce(where, function(result, value, key) {
    if (_.contains(jsonkeys, key.split('.')[0])) {
      result[key] = value;
    }
    return result;
  }, {});

  var whereRaw = '';
  _.each(where, function(value, key) {
    var tab = key.split('.');

    if (!whereRaw) {
      whereRaw = tab[0] + '->>\'' + tab[1] + '\' = \'' + value + '\'';
    } else {
      whereRaw += ' AND ' + tab[0] + '->>\'' + tab[1] + '\' = \'' + value + '\'';
    }
  });

  return whereRaw;
};

'use strict';

var _ = require('lodash');

/**
* Format where expression.
*
* @param {string[]|string} keys Array of keys to omit in query
* @param {string[]|string} query Array of parameter to use in where
* @param {string[]|string} jsonkeys Array of json keys to use in query
*/

module.exports = function (keys, query, jsonkeys) {
  var where = _.omit(query, keys);

  /**
   * Omit all keys in 'where' who not value jonskeys[] + '.'
   * Exemple:
   *   where = {id :5, data.team: 456}
   *   jsonkeys = [data]
   * Result where = {data.team: 456} why data.team contains data.
  */
  where = _.reduce(where, function(result, value, key) {
    if (_.contains(jsonkeys, key.split('.')[0])) {
      result[key] = value;
    }
    return result;
  }, {});

  var whereRaw = '';
  _.each(where, function(value, key) {
    var tab = key.split('.');

    if (!whereRaw) {
      whereRaw = tab[0] + '->>\'' + tab[1] + '\' = \'' + value + '\'';
    } else {
      whereRaw += ' AND ' + tab[0] + '->>\'' + tab[1] + '\' = \'' + value + '\'';
    }
  });

  return whereRaw;
};

Change comment to json key


import fmt from "./fmt"
import { log } from "fly-util"

export default function () {
  this.on("fly_run", ({ path }) =>
    log(`Flying with ${fmt.path}...`, path))

  .on("flyfile_not_found", ({ error }) =>
    log(`No Flyfile Error: ${fmt.error}`, error))

  .on("fly_watch", () =>
    log(`${fmt.warn}`, "Watching files..."))

  .on("plugin_load", ({ plugin }) =>
    log(`Loading plugin ${fmt.name}`, plugin))

  .on("plugin_error", ({ plugin, error }) =>
    log(`${fmt.error} failed due to ${fmt.error}`, plugin, error))

  .on("task_error", ({ task, error }) =>
    log(`${fmt.error} failed due to ${fmt.error}`, task, error))

  .on("task_start", ({ task }) =>
    log(`Starting ${fmt.start}`, task))

  .on("task_complete", ({ task, duration }) =>
    log(`Finished ${fmt.complete} in ${fmt.secs}`, task, duration, "ms"))

  .on("task_not_found", ({ task }) =>
    log(`${fmt.error} not found in Flyfile.`, task))

  return this
}

import fmt from "./fmt"
import { log } from "fly-util"

export default function () {
  this.on("fly_run", ({ path }) =>
    log(`Flying with ${fmt.path}...`, path))

  .on("flyfile_not_found", ({ error }) =>
    log(`No Flyfile Error: ${fmt.error}`, error))

  .on("fly_watch", () =>
    log(`${fmt.warn}`, "Watching files..."))

  .on("plugin_load", ({ plugin }) =>
    log(`Loading plugin ${fmt.name}`, plugin))

  .on("plugin_error", ({ plugin, error }) =>
    log(`${fmt.error} failed due to ${fmt.error}`, plugin, error))

  .on("task_error", ({ task, error }) =>
    log(`${fmt.error} failed due to ${fmt.error}`, task, error))

  .on("task_start", ({ task }) =>
    log(`Starting ${fmt.start}`, task))

 .on("task_complete", ({ task, duration }) => {
    const time = timeInfo(duration)
    log(`Finished ${fmt.complete} in ${fmt.secs}`, task, time.duration, time.scale)
 })

  .on("task_not_found", ({ task }) =>
    log(`${fmt.error} not found in Flyfile.`, task))

  return this
}

/**
 * conditionally format task duration
 * @param  {Number} duration task duration in ms
 * @param  {String} scale default scale for output
 * @return {Object} time information
 */
function timeInfo (duration, scale = "ms") {
  const time = duration >= 1000
    ? { duration: Math.round((duration / 1000) * 10) / 10, scale: "s" }
    : { duration, scale }
  return time
}

Add timeInfo() for conditional scales


const $ = require("jquery");

function isInViewport() {}
$.extend($.expr[":"], {
    "in-viewport": $.expr.createPseudo
        ? $.expr.createPseudo(argsString => currElement => isInViewport(currElement, argsString))
        : (currObj, index, meta) => isInViewport(currObj, meta)
})

function run() {}
$.fn.run = run

const $ = require("jquery");

// always returns true, indicating that every tested element is in viewport
function isInViewport() {
    return true;
}
$.extend($.expr[":"], {
    "in-viewport": $.expr.createPseudo
        ? $.expr.createPseudo(argsString => currElement => isInViewport(currElement, argsString))
        : (currObj, index, meta) => isInViewport(currObj, meta)
})

Fix is-in-viewport mock to return true


var path = require('path');

module.exports = function(config) {
    config.set({
        browsers: ['Chrome'],
        coverageReporter: {
            reporters: [
                {
                    type: 'html',
                    subdir: 'html'
                }, {
                    type: 'lcovonly',
                    subdir: '.'
                }
            ]
        },
        files: [
            'node_modules/babel-polyfill/dist/polyfill.js', 'tests.webpack.js'
        ],
        frameworks: ['jasmine'],
        preprocessors: {
            'tests.webpack.js': ['webpack', 'sourcemap']
        },
        reporters: [
            'progress', 'coverage'
        ],
        webpack: {
            cache: true,
            devtool: 'inline-source-map',
            module: {
                preLoaders: [
                    {
                        test: /.spec\.js$/,
                        include: /calculations/,
                        exclude: /(bower_components|node_modules)/,
                        loader: 'babel',
                        query: {
                            cacheDirectory: true
                        }
                    }, {
                        test: /\.js?$/,
                        include: /calculations/,
                        exclude: /(node_modules|bower_components|__spec__)/,
                        loader: 'babel-istanbul',
                        query: {
                            cacheDirectory: true
                        }
                    }
                ],
                loaders: [
                    {
                        test: /\.js$/,
                        include: path.resolve(__dirname, '../calculations'),
                        exclude: /(bower_components|node_modules|__spec__)/,
                        loader: 'babel',
                        query: {
                            cacheDirectory: true
                        }
                    }
                ]
            }
        }
    });
};

var path = require('path');

module.exports = function(config) {
    config.set({
        browsers: ['PhantomJS'],
        coverageReporter: {
            reporters: [
                {
                    type: 'html',
                    subdir: 'html'
                }, {
                    type: 'lcovonly',
                    subdir: '.'
                }
            ]
        },
        files: [
            'node_modules/babel-polyfill/dist/polyfill.js', 'tests.webpack.js'
        ],
        frameworks: ['jasmine'],
        preprocessors: {
            'tests.webpack.js': ['webpack', 'sourcemap']
        },
        reporters: [
            'progress', 'coverage'
        ],
        webpack: {
            cache: true,
            devtool: 'inline-source-map',
            module: {
                preLoaders: [
                    {
                        test: /.spec\.js$/,
                        include: /calculations/,
                        exclude: /(bower_components|node_modules)/,
                        loader: 'babel',
                        query: {
                            cacheDirectory: true
                        }
                    }, {
                        test: /\.js?$/,
                        include: /calculations/,
                        exclude: /(node_modules|bower_components|__spec__)/,
                        loader: 'babel-istanbul',
                        query: {
                            cacheDirectory: true
                        }
                    }
                ],
                loaders: [
                    {
                        test: /\.js$/,
                        include: path.resolve(__dirname, '../calculations'),
                        exclude: /(bower_components|node_modules|__spec__)/,
                        loader: 'babel',
                        query: {
                            cacheDirectory: true
                        }
                    }
                ]
            }
        }
    });
};

Change karma browser for tests from Chrome to PhantomJS


module.exports = function(data){
  let d = {},
    keys = Object.keys(data);

  for (let i = 0; i < keys.length; i++) {
    let key = keys[i],
      value = data[key],
      current = d,
      keyParts = key
        .replace(new RegExp(/\[/g), '.')
        .replace(new RegExp(/\]/g), '')
        .split('.');

    for (let index = 0; index < keyParts.length; index++){
      let k = keyParts[index];
      if (index >= keyParts.length - 1){
        current[k] = value;
      } else {
        if (!current[k]) current[k] = !isNaN(keyParts[index + 1]) ? [] : {};
        current = current[k];
      }
    }
  }

  return d;
};

module.exports = function(data){
  if (!data || data.length < 1) return {};

  let d = {},
    keys = Object.keys(data);

  for (let i = 0; i < keys.length; i++) {
    let key = keys[i],
      value = data[key],
      current = d,
      keyParts = key
        .replace(new RegExp(/\[/g), '.')
        .replace(new RegExp(/\]/g), '')
        .split('.');

    for (let index = 0; index < keyParts.length; index++){
      let k = keyParts[index];
      if (index >= keyParts.length - 1){
        current[k] = value;
      } else {
        if (!current[k]) current[k] = !isNaN(keyParts[index + 1]) ? [] : {};
        current = current[k];
      }
    }
  }

  return d;
};

Return empty when data is empty


//  Copyright (c) 2014 Readium Foundation and/or its licensees. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met:
//  1. Redistributions of source code must retain the above copyright notice, this
//  list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright notice,
//  this list of conditions and the following disclaimer in the documentation and/or
//  other materials provided with the distribution.
//  3. Neither the name of the organization nor the names of its contributors may be
//  used to endorse or promote products derived from this software without specific
//  prior written permission.

window.process = {};

window.process._RJS_baseUrl = function(n)
{
    return "..";
};

window.process._RJS_rootDir = function(n)
{
    if (n == 0) return ".";
};

require.config({

    /* http://requirejs.org/docs/api.html#config-waitSeconds */
    waitSeconds: 1
});

//  Copyright (c) 2014 Readium Foundation and/or its licensees. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met:
//  1. Redistributions of source code must retain the above copyright notice, this
//  list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright notice,
//  this list of conditions and the following disclaimer in the documentation and/or
//  other materials provided with the distribution.
//  3. Neither the name of the organization nor the names of its contributors may be
//  used to endorse or promote products derived from this software without specific
//  prior written permission.

window.process = {};

window.process._RJS_baseUrl = function(n)
{
    return "..";
};

window.process._RJS_rootDir = function(n)
{
    if (n == 0) return ".";
};

window.process._RJS_isBrowser = true;

require.config({

    /* http://requirejs.org/docs/api.html#config-waitSeconds */
    waitSeconds: 1
});

Set a flag when config is loaded on a browser


import run from "ember-metal/run_loop";
import View from "ember-views/views/view";

QUnit.module("Ember.View additions to run queue");

QUnit.skip("View hierarchy is done rendering to DOM when functions queued in afterRender execute", function() {
  var didInsert = 0;
  var childView = View.create({
    elementId: 'child_view',
    didInsertElement() {
      didInsert++;
    }
  });
  var parentView = View.create({
    elementId: 'parent_view',
    render(buffer) {
      this.appendChild(childView);
    },
    didInsertElement() {
      didInsert++;
    }
  });

  run(function() {
    parentView.appendTo('#qunit-fixture');
    run.schedule('afterRender', this, function() {
      equal(didInsert, 2, 'all didInsertElement hooks fired for hierarchy');
    });
  });

  run(function() {
    parentView.destroy();
  });
});

import run from "ember-metal/run_loop";
import View from "ember-views/views/view";
import { compile } from "ember-template-compiler";

QUnit.module("Ember.View additions to run queue");

QUnit.test("View hierarchy is done rendering to DOM when functions queued in afterRender execute", function() {
  var didInsert = 0;
  var childView = View.create({
    elementId: 'child_view',
    didInsertElement() {
      didInsert++;
    }
  });
  var parentView = View.create({
    elementId: 'parent_view',
    template: compile("{{view view.childView}}"),
    childView: childView,
    didInsertElement() {
      didInsert++;
    }
  });

  run(function() {
    parentView.appendTo('#qunit-fixture');
    run.schedule('afterRender', this, function() {
      equal(didInsert, 2, 'all didInsertElement hooks fired for hierarchy');
    });
  });

  run(function() {
    parentView.destroy();
  });
});

Use template instead of render buffer


import { defineConfig } from "vite";
import { metablock } from "vite-plugin-userscript";
import manifest from "./package.json" assert { type: "json" };

const isDevBuild = process.env.NODE_ENV === "development";

function getDateString() {
  const date = new Date();
  const year = date.getFullYear();
  const month = `${date.getMonth() + 1}`.padStart(2, "0");
  const day = `${date.getDate()}`.padStart(2, "0");
  return `${year}${month}${day}`;
}

const filename = [
  "kitten-scientists",
  isDevBuild ? "-dev" : "",
  process.env.NIGHTLY_BUILD ? `-${getDateString()}` : "",
  process.env.GITHUB_SHA ? `-${String(process.env.GITHUB_SHA).substring(0, 7)}` : "",
  ".user.js",
].join("");

export default defineConfig({
  plugins: [
    metablock({
      override: {
        version: manifest.version,
        description: manifest.description,
        homepage: manifest.homepage,
        supportURL: manifest.bugs.url,
      },
    }),
  ],
  build: {
    lib: {
      entry: "source/index.ts",
      name: "kitten-scientists",
    },
    minify: isDevBuild ? "esbuild" : false,
    outDir: "output",
    rollupOptions: {
      output: {
        extend: true,
        format: "umd",
        entryFileNames: filename,
      },
    },
  },
});

import { defineConfig } from "vite";
import { metablock } from "vite-plugin-userscript";
import manifest from "./package.json" assert { type: "json" };

const isDevBuild = process.env.NODE_ENV === "development";

function getDateString() {
  const date = new Date();
  const year = date.getFullYear();
  const month = `${date.getMonth() + 1}`.padStart(2, "0");
  const day = `${date.getDate()}`.padStart(2, "0");
  return `${year}${month}${day}`;
}

const filename = [
  "kitten-scientists",
  isDevBuild ? "-dev" : `-${manifest.version}`,
  process.env.NIGHTLY_BUILD ? `-${getDateString()}` : "",
  process.env.GITHUB_SHA ? `-${String(process.env.GITHUB_SHA).substring(0, 7)}` : "",
  ".user.js",
].join("");

export default defineConfig({
  plugins: [
    metablock({
      override: {
        version: manifest.version,
        description: manifest.description,
        homepage: manifest.homepage,
        supportURL: manifest.bugs.url,
      },
    }),
  ],
  build: {
    lib: {
      entry: "source/index.ts",
      name: "kitten-scientists",
    },
    minify: isDevBuild ? "esbuild" : false,
    outDir: "output",
    rollupOptions: {
      output: {
        extend: true,
        format: "umd",
        entryFileNames: filename,
      },
    },
  },
});

Put baseline version in filename


import { FREE_SUBSCRIPTION } from 'proton-shared/lib/constants';
import { SubscriptionModel } from 'proton-shared/lib/models/subscriptionModel';
import { UserModel } from 'proton-shared/lib/models/userModel';

import useCachedModelResult from './useCachedModelResult';
import useApi from '../containers/api/useApi';
import useCache from '../containers/cache/useCache';

const useSubscription = () => {
    const cache = useCache();
    const api = useApi();

    return useCachedModelResult(cache, SubscriptionModel.key, () => {
        // Not using use user since it's better to read from the cache
        const user = cache.get(UserModel.key).value;
        if (user.isAdmin) {
            return SubscriptionModel.get(api);
        }
        return Promise.resolve(FREE_SUBSCRIPTION);
    });
};

export default useSubscription;

import { FREE_SUBSCRIPTION } from 'proton-shared/lib/constants';
import { SubscriptionModel } from 'proton-shared/lib/models/subscriptionModel';
import { UserModel } from 'proton-shared/lib/models/userModel';

import useCachedModelResult from './useCachedModelResult';
import useApi from '../containers/api/useApi';
import useCache from '../containers/cache/useCache';

const useSubscription = () => {
    const cache = useCache();
    const api = useApi();

    return useCachedModelResult(cache, SubscriptionModel.key, () => {
        const { value: user = {} } = cache.get(UserModel.key) || {};
        if (user.isAdmin) {
            return SubscriptionModel.get(api);
        }
        return Promise.resolve(FREE_SUBSCRIPTION);
    });
};

export default useSubscription;

Fix special case for admin


import React, {PropTypes} from 'react'
import classNames from 'classnames'

const VisHeader = ({views, view, onToggleView, name}) => (

      {views.length
        ?

            {views.map(v => (

 onToggleView(v)}
                className={classNames('toggle-btn ', {active: view === v})}
              >
                {v}

            ))}

        : null}

{name}

)

const {arrayOf, func, string} = PropTypes

VisHeader.propTypes = {
  views: arrayOf(string).isRequired,
  view: string.isRequired,
  onToggleView: func.isRequired,
  name: string.isRequired,
}

export default VisHeader

Remove unused div and style


import React from 'react';
import ReactDOM from 'react-dom';
import {Router, Route, browserHistory, IndexRoute} from 'react-router'
import App from './components/app';
import Login from './components/login'
import Signup from './components/signup'
import Classes from './components/teacher/classes/Classes'
import Lessons from './components/teacher/classes/ClassData/ClassData'
import LessonsData from './components/teacher/classes/ClassData/LessonData'
import Students from './components/teacher/classes/students/StudentData'
import Settings from './components/Settings'

ReactDOM.render((
  <Router history={browserHistory}>
    <Route path='/' component = {App}>
      <IndexRoute component = {Classes} />
        <Route path='/class/:classId/lessons/' component={Lessons} />
        <Route path='/class/:classId/lessons/:lessonId' component={LessonsData}/>
        <Route path='/class/:classId/students/:studentId' component={Students}/>
      </Route>
    </Route>
  </Router>
), document.getElementById("app"));

import React from 'react';
import ReactDOM from 'react-dom';
import {Router, Route, browserHistory, IndexRoute} from 'react-router'
import App from './components/app';
import Login from './components/login'
import Signup from './components/signup'
import Classes from './components/teacher/classes/Classes'
import Lessons from './components/teacher/classes/ClassData/ClassData'
import LessonsData from './components/teacher/classes/ClassData/LessonData'
import Students from './components/teacher/classes/students/StudentData'
import Settings from './components/Settings'

ReactDOM.render((
  <Router history={browserHistory}>
    <Route path='/' component = {App}>
      <IndexRoute component = {Classes} />
        <Route path='class/:classId/lessons' component={Lessons} />
        <Route path='class/:classId/lessons/:lessonId' component={LessonsData}/>
        <Route path='class/:classId/students/:studentId' component={Students}/>
      </Route>
  </Router>
), document.getElementById("app"));

Remove starting and trailing slashes


var HyperdeckCore = require("./hyperdeck-core.js");

var Hyperdeck = function(ip) {
  //Start by connecting to Hyperdeck via HypderdeckCore
  var Core = new HyperdeckCore(ip);

  //Publicise functions from Core
  this.getNotifier = Core.getNotifier();
  this.makeRequest = Core.makeRequest();
  this.onConnected = Core.onConnected();

  //make Easy Access commands
  this.play = function(speed) {
    var commandString;
    if (Math.abs(speed) <= 1600) {
      commandString = "play speed: " + speed;
    } else {
      if (speed) {
        throw "Speed out of range";
      } else {
        commandString = "play";
      }
    }
    return Core.makeRequest(commandString);
  };

  this.stop = function() {
    return Core.makeRequest("stop");
  };

  this.record = function() {
    return Core.makeRequest("record");
  };

  this.goTo = function(timecode) {
    return Core.makeRequest("goto: timecode: " + timecode);
  };

};

module.exports = Hyperdeck;

var HyperdeckCore = require("./hyperdeck-core.js");

var Hyperdeck = function(ip) {
  //Start by connecting to Hyperdeck via HypderdeckCore
  var Core = new HyperdeckCore(ip);
  Core.makeRequest("notify: remote: true");
  Core.makeRequest("notify: transport: true");
  Core.makeRequest("notify: slot: true");
  Core.makeRequest("notify: configuration: true");

  //Publicise functions from Core
  this.getNotifier = Core.getNotifier();
  this.makeRequest = Core.makeRequest();
  this.onConnected = Core.onConnected();

  //make Easy Access commands
  this.play = function(speed) {
    var commandString;
    if (Math.abs(speed) <= 1600) {
      commandString = "play speed: " + speed;
    } else {
      if (speed) {
        throw "Speed out of range";
      } else {
        commandString = "play";
      }
    }
    return Core.makeRequest(commandString);
  };

  this.stop = function() {
    return Core.makeRequest("stop");
  };

  this.record = function() {
    return Core.makeRequest("record");
  };

  this.goTo = function(timecode) {
    return Core.makeRequest("goto: timecode: " + timecode);
  };

};

module.exports = Hyperdeck;

Enable asynchronous notifications from Hyperdeck


angular
  .module('zibble', ['angular-jwt', 'ngResource', 'ui.router'])
  .constant('API', 'http://localhost:3000')
  .config(function($httpProvider){
    $httpProvider.interceptors.push('AuthInterceptor');
  })
  .config(MainRouter);

function MainRouter($stateProvider, $urlRouterProvider, $locationProvider){
  $stateProvider
    .state('home', {
      url         : '/',
      templateUrl : './views/home.html'
    });

  $stateProvider
    .state('login', {
      url         : '/login',
      templateUrl : './views/login.html'
    });

  $stateProvider
    .state('signup', {
      url         : '/signup',
      templateUrl : './views/signup.html'
    });

  $stateProvider
    .state('logout', {
      url         : '/logout',
      templateUrl : './views/logout.html'
    });

  $urlRouterProvider.otherwise('/');

  $locationProvider.html5Mode({
    enabled: true,
    requireBase: false
  });
}

angular
  .module('zibble', ['angular-jwt', 'ngResource', 'ui.router'])
  .constant('API', 'https://zibble-back-end.herokuapp.com')
  .config(function($httpProvider){
    $httpProvider.interceptors.push('AuthInterceptor');
  })
  .config(MainRouter);

function MainRouter($stateProvider, $urlRouterProvider, $locationProvider){
  $stateProvider
    .state('home', {
      url         : '/',
      templateUrl : './views/home.html'
    });

  $stateProvider
    .state('login', {
      url         : '/login',
      templateUrl : './views/login.html'
    });

  $stateProvider
    .state('signup', {
      url         : '/signup',
      templateUrl : './views/signup.html'
    });

  $stateProvider
    .state('logout', {
      url         : '/logout',
      templateUrl : './views/logout.html'
    });

  $urlRouterProvider.otherwise('/');

  $locationProvider.html5Mode({
    enabled: true,
    requireBase: false
  });
}

Set API to Heroku address.


/**
 * Simple wrapper to wrap around the Cacheman nodejs package to integrate easily with SailsJS for caching.
 * @param {[string]} name Name the Cache Instance.
 */

var Cache = function (name) {
   var Cacheman = require('cacheman');
   var _ = require('underscore');
   var options = {};
   // Get configuration
   config = sails.config.cacheman;
   if (config === undefined) {
        throw new Error('No configuration file found. Please add the configuration app/config/cacheman.js');
   }
   // if a valid driver is selected.
   if(_.indexOf(['memory', 'redis', 'mongo', 'file'], config.driver) < 0) {
        throw new Error("Invalid Driver selected. Please choose from ('memory', 'redis', 'mongo', 'file')");
   }

   var cache = new Cacheman(name, config[config.driver]);

   return cache;
};
module.exports = Cache;

/**
 * Simple wrapper to wrap around the Cacheman nodejs package to integrate easily with SailsJS for caching.
 * @param {[string]} name Name the Cache Instance.
 */

var Cache = function (name) {
   var Cacheman = require('cacheman');
   var _ = require('underscore');
   var options = {};
   // Get configuration
   var config = sails.config.cacheman;
   if (config === undefined) {
        throw new Error('No configuration file found. Please add the configuration app/config/cacheman.js');
   }
   // if a valid driver is selected.
   if(_.indexOf(['memory', 'redis', 'mongo', 'file'], config.driver) < 0) {
        throw new Error("Invalid Driver selected. Please choose from ('memory', 'redis', 'mongo', 'file')");
   }

   var cache = new Cacheman(name, config[config.driver]);

   return cache;
};
module.exports = Cache;

Fix config variable exposing to global scope


class LivechatExternalMessage extends RocketChat.models._Base {
	constructor() {
		super('livechat_external_message');
	}

	// FIND
	findByRoomId(roomId, sort = { ts: -1 }) {
		const query = { rid: roomId };

		return this.find(query, { sort: sort });
	}
}

RocketChat.models.LivechatExternalMessage = new LivechatExternalMessage();

class LivechatExternalMessage extends RocketChat.models._Base {
	constructor() {
		super('livechat_external_message');

		if (Meteor.isClient) {
			this._initModel('livechat_external_message');
		}
	}

	// FIND
	findByRoomId(roomId, sort = { ts: -1 }) {
		const query = { rid: roomId };

		return this.find(query, { sort: sort });
	}
}

RocketChat.models.LivechatExternalMessage = new LivechatExternalMessage();

Fix livechat knowledge base by properly initiating it's model


import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import Box from '../box';
import cx from 'classnames';
import theme from './theme.css';

class Avatar extends PureComponent {
  static propTypes = {
    /** Component that will be placed top right of the avatar image. */
    children: PropTypes.any,
    /** A class name for the wrapper to give custom styles. */
    className: PropTypes.string,
    /** An image source or an image element. */
    image: PropTypes.string,
    /** An alternative text for the image element. */
    imageAlt: PropTypes.string,
    /** A class name for the image to give custom styles. */
    imageClassName: PropTypes.string,
    /** The size of the avatar. */
    size: PropTypes.oneOf(['tiny', 'small', 'medium']),
  };

  static defaultProps = {
    size: 'medium',
  };

  render() {
    const { children, className, image, imageAlt, imageClassName, size, ...others } = this.props;

    const avatarClassNames = cx(theme['avatar'], theme[size], className);

    return (
      <Box className={avatarClassNames} {...others} data-teamleader-ui="avatar">

        {children &&
{children}
}
      </Box>
    );
  }
}

export default Avatar;

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import Box from '../box';
import cx from 'classnames';
import theme from './theme.css';

class Avatar extends PureComponent {
  render() {
    const { children, className, image, imageAlt, imageClassName, size, ...others } = this.props;

    const avatarClassNames = cx(theme['avatar'], theme[size], className);

    return (
      <Box className={avatarClassNames} {...others} data-teamleader-ui="avatar">

        {children &&
{children}
}
      </Box>
    );
  }
}

Avatar.propTypes = {
  /** Component that will be placed top right of the avatar image. */
  children: PropTypes.any,
  /** A class name for the wrapper to give custom styles. */
  className: PropTypes.string,
  /** An image source or an image element. */
  image: PropTypes.string,
  /** An alternative text for the image element. */
  imageAlt: PropTypes.string,
  /** A class name for the image to give custom styles. */
  imageClassName: PropTypes.string,
  /** The size of the avatar. */
  size: PropTypes.oneOf(['tiny', 'small', 'medium']),
};

Avatar.defaultProps = {
  size: 'medium',
};

export default Avatar;

Move static propTypes & defaultProps outside of the component class


module.exports = ['$sce', $sce => {
  return {
    restrict: 'E',
    template: require('./template.html'),
    controller: ['$scope', $scope => {
      $scope.video = {src: undefined};

      $scope.receivedPaste = $event => {
        console.log($event, $event.clipboardData.getData('text/plain'));
      };

      $scope.droppedFiles = function($files, $items) {
        const file = $files[0];

        if (file) {
          const type = file.type;

          if (type.indexOf('video') === 0) {
            $scope.video.src = $sce.trustAsResourceUrl(URL.createObjectURL(file));
          }
        }
      };
    }]
  };
}];

module.exports = ['$sce', $sce => {
  return {
    restrict: 'E',
    template: require('./template.html'),
    controller: ['$scope', $scope => {
      $scope.video = {src: undefined};

      $scope.receivedPaste = $event => {
        console.log($event, $event.clipboardData.getData('text/plain'));
      };

      $scope.droppedFiles = function($files, $items) {
        const file = $files[0];

        if (file) {
          const type = file.type;

          if (type.indexOf('video') === 0) {
            $scope.video.src = $sce.trustAsResourceUrl(URL.createObjectURL(file));
            $scope.video.loaded = false;
          }
        }
      };
    }]
  };
}];

Reset loaded flag on new video. (Must be a better way to do this)


/* eslint-disable import/no-extraneous-dependencies */

import React, { Fragment } from 'react';
import { FormGroup } from 'react-bootstrap';

import Control from '../components/Control.react';
import { Typeahead } from '../../src';
import options from '../exampleData';

/* example-start */
class BasicExample extends React.Component {
  state = {
    multiple: false,
  };

  render() {
    const { multiple } = this.state;

    return (
      <Fragment>
        <Typeahead
          labelKey="name"
          multiple={multiple}
          options={options}
          placeholder="Choose a state..."
        />
        <FormGroup>
          <Control
            checked={multiple}
            onChange={(e) => this.setState({ multiple: e.target.checked })}
            type="checkbox">
            Multi-Select
          </Control>
        </FormGroup>
      </Fragment>
    );
  }
}
/* example-end */

export default BasicExample;

/* eslint-disable import/no-extraneous-dependencies */

import React, { Fragment } from 'react';
import { FormGroup } from 'react-bootstrap';

import Control from '../components/Control.react';
import { Typeahead } from '../../src';
import options from '../exampleData';

/* example-start */
class BasicExample extends React.Component {
  state = {
    multiple: false,
    selected: [],
  };

  render() {
    return (
      <Fragment>
        <Typeahead
          {...this.state}
          labelKey="name"
          onChange={(selected) => this.setState({ selected })}
          options={options}
          placeholder="Choose a state..."
        />
        <FormGroup>
          <Control
            checked={this.state.multiple}
            onChange={(e) => this.setState({ multiple: e.target.checked })}
            type="checkbox">
            Multi-Select
          </Control>
        </FormGroup>
      </Fragment>
    );
  }
}
/* example-end */

export default BasicExample;

Convert basic example to be controlled


function SortingController(){

}
SortingController.prototype = {
  getFormData: function(form){
   var formData = {};
   formData.type = $(form).attr("method");
   formData.url = $(form).attr("action");
   formData.data = $(form).serialize();
   return formData;
  },

};

function SortingController(){
  this.view = new RiverView();
}
SortingController.prototype = {
  orderRiver: function(eventTarget){
    var that = this;
    $.ajax($(eventTarget).attr("href")).done(function(newRiverDiv){
      that.view.refreshRiver(newRiverDiv);
    }).fail(function(error){
      alert(error.responseText);
    });
  }
};

Add orderRiver method to SortingController


import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';

/* eslint-disable object-shorthand */

Template.If_Admin_Logged_In.helpers({
  /**
   * @returns {*} True if Meteor is in the process of logging in.
   */
  authInProcess: function authInProcess() {
    return Meteor.loggingIn();
  },
  /**
   * @returns {boolean} True if there is a logged in user.
   */
  canShow: function canShow() {
    return !!Meteor.user();
  },
});

import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';

Template.If_Admin_Logged_In.helpers({

   // @returns {*} True if Meteor is in the process of logging in.
   //
  authInProcess: function authInProcess() {
    return Meteor.loggingIn();
  },
   //@returns {boolean} True if there is a logged in user.

  canShow: function canShow() {
    return !!Meteor.user();
  },
});

Modify settings.json to allow deploy


'use strict';

var App = require('../../app');
var Marionette = require('backbone.marionette');
var template = require('../templates/bookmark_item.hbs');

module.exports = Marionette.ItemView.extend({
  tagName: 'li',
  template: template,
  events: {
    'click .js-remove-bookmark': function(event) {
      App.request('deleteBookmark', this.model.get('module'));
      this.model.collection.remove(this.model);
    }
  }
});

'use strict';

var App = require('../../app');
var Marionette = require('backbone.marionette');
var template = require('../templates/bookmark_item.hbs');

module.exports = Marionette.ItemView.extend({
  tagName: 'li',
  template: template,
  events: {
    'click .js-remove-bookmark': function (event) {
      App.request('deleteBookmark', this.model.get('module'));
      this.model.collection.remove(this.model);
    },
    'click a': function () {
      $('.nm-bookmark-button').qtip('hide');
    }
  }
});

Hide bookmarks qtip after navigating to a module


var Proteus = require("proteus");

module.exports = Proteus.Class.derive(Object.defineProperties({

    init: function () {
        Object.defineProperties(this, {
            members: { value: [], enumerable: true }
        });

        Proteus.slice(arguments).forEach(this.add, this);
    },

    add: function (exp) {
        return this.members.push(exp);
    },

    remove: function (idx) {
        return this.members.splice(idx, 1);
    },

    reset: function () {
        this.members.length = 0;
        return this;
    }

}, {
    length: {
        get: function () {
            return this.members.length;
        },
        set: function (v) {
            this.members.length = v;
        }
    },

    valueOf: { value: function () {
        var results = [],
            members = this.members,
            len     = members.length,
            i
        ;

        for (i = 0; i < len; i++) {
            results.push(members[i].valueOf());
        }

        return results.length <= 1 ? results[0] : results;
    } }
}));

var Proteus = require("proteus");

module.exports = Proteus.Class.derive(Object.defineProperties({

    init: function () {
        this.members = [];
        Proteus.slice(arguments).forEach(this.add, this);
    },

    get: function (idx) {
        return this.members[idx];
    },

    add: function (exp) {
        return this.members.push(exp);
    },

    remove: function (idx) {
        return this.members.splice(idx, 1);
    },

    reset: function () {
        this.members.length = 0;
        return this;
    }

}, {
    count: {
        get: function () {
            return this.members.length;
        },
        set: function (v) {
            this.members.length = v;
        }
    },

    valueOf: { value: function () {
        var results = [],
            len     = this.count,
            i
        ;

        for (i = 0; i < len; i++) {
            results.push(this.get(i).valueOf());
        }

        return results.length <= 1 ? results[0] : results;
    } }
}));

Use basic properties and make public properties consistent with other interfaces.


(function() {

  "use strict";

  window.addEventListener('message', function(event) {
    if (event.data === 'debugger-client') {
      var port = event.ports[0];
      listenToPort(port);
    } else if (event.data.type) {
      chrome.extension.sendMessage(event.data);
    }
  });

  function listenToPort(port) {
    port.addEventListener('message', function(event) {
      chrome.extension.sendMessage(event.data);
    });

    chrome.extension.onMessage.addListener(function(message) {
      if (message.from === 'devtools') {
        port.postMessage(message);
      }
    });

    port.start();
  }

  // let ember-debug know that content script has executed
  document.documentElement.dataset.emberExtension = 1;

  // clear a possible previous Ember icon
  chrome.extension.sendMessage({ type: 'resetEmberIcon' });

  // inject JS into the page to check for an app on domready
  var script = document.createElement('script');
  script.type = "text/javascript";
  script.src = chrome.extension.getURL("in-page-script.js");
  if (document.body) document.body.appendChild(script);

  var iframes = document.getElementsByTagName('iframe');
  var urls = [];
  for (var i = 0, l = iframes.length; i < l; i ++) {
    urls.push(iframes[i].src);
  }

  // FIXME
  setTimeout(function() {
    chrome.extension.sendMessage({type: 'iframes', urls: urls});
  }, 500);

}());

(function() {

  "use strict";

  window.addEventListener('message', function(event) {
    if (event.data === 'debugger-client') {
      var port = event.ports[0];
      listenToPort(port);
    } else if (event.data.type) {
      chrome.extension.sendMessage(event.data);
    }
  });

  function listenToPort(port) {
    port.addEventListener('message', function(event) {
      chrome.extension.sendMessage(event.data);
    });

    chrome.extension.onMessage.addListener(function(message) {
      if (message.from === 'devtools') {
        port.postMessage(message);
      }
    });

    port.start();
  }

  // let ember-debug know that content script has executed
  document.documentElement.dataset.emberExtension = 1;

  // clear a possible previous Ember icon
  chrome.extension.sendMessage({ type: 'resetEmberIcon' });

  // inject JS into the page to check for an app on domready
  var script = document.createElement('script');
  script.type = "text/javascript";
  script.src = chrome.extension.getURL("in-page-script.js");
  if (document.body) {
    document.body.appendChild(script);
    script.onload = function() {
      document.body.removeChild(script);
    }
  }

  var iframes = document.getElementsByTagName('iframe');
  var urls = [];
  for (var i = 0, l = iframes.length; i < l; i ++) {
    urls.push(iframes[i].src);
  }

  // FIXME
  setTimeout(function() {
    chrome.extension.sendMessage({type: 'iframes', urls: urls});
  }, 500);

}());

Remove tomster script after injection


function setCookie(name, value, options = {}) {
  let cookieString = `${name}=${value}; path=/`;

  if (options.days) {
    const date = new Date();
    date.setTime(date.getTime() + (options.days * 24 * 60 * 60 * 1000));
    cookieString = `${cookieString}; expires=${date.toGMTString()}`;
  }

  if (document.location.protocol === 'https:') {
    cookieString = `${cookieString}; Secure`;
  }

  document.cookie = cookieString;
}

function getCookie(name) {
  const nameEQ = `${name}=`;
  const cookies = document.cookie.split(';');

  for (let i = 0, len = cookies.length; i < len; i++) {
    let cookie = cookies[i];

    while (cookie.charAt(0) === ' ') {
      cookie = cookie.substring(1, cookie.length);
    }

    if (cookie.indexOf(nameEQ) === 0) {
      return decodeURIComponent(cookie.substring(nameEQ.length));
    }
  }

  return null;
}

module.exports = (id) => {
  const banner = document.getElementById(id);

  if (banner && getCookie('seen_cookie_message') === null) {
    banner.style.display = 'block';
    setCookie('seen_cookie_message', 'yes', { days: 28 });
  }
};

function setCookie(name, value, options = {}) {
  let cookieString = `${name}=${value}; path=/`;

  if (options.days) {
    const date = new Date();
    date.setTime(date.getTime() + (options.days * 24 * 60 * 60 * 1000));
    cookieString = `${cookieString}; expires=${date.toGMTString()}`;
  }

  if (document.location.protocol === 'https:') {
    cookieString = `${cookieString}; Secure`;
  }

  document.cookie = cookieString;
}

function getCookie(name) {
  const nameEQ = `${name}=`;
  const cookies = document.cookie.split(';');

  for (let i = 0, len = cookies.length; i < len; i++) {
    let cookie = cookies[i];

    while (cookie.charAt(0) === ' ') {
      cookie = cookie.substring(1, cookie.length);
    }

    if (cookie.indexOf(nameEQ) === 0) {
      return decodeURIComponent(cookie.substring(nameEQ.length));
    }
  }

  return null;
}

module.exports = (id) => {
  const banner = document.getElementById(id);

  if (banner && getCookie('nhsuk_seen_cookie_message') === null) {
    banner.style.display = 'block';
    setCookie('seen_cookie_message', 'yes', { days: 28 });
  }
};

Rename cookie message cookie name It currently clashes with GOV.UK cookie name so if the user has previously visited GOV.UK they won't see our cookie message.