import Select from '../u-select.vue';

export default {
    name: 'u-select--0',
    childName: 'u-select--0-item',
    mixins: [Select],
    methods: {
        select($event) {
            const oldValue = this.value;
            const value = $event.target.value;
            this.selectedVM = this.itemVMs.find((itemVM) => itemVM.value === value);
            const item = this.selectedVM && this.selectedVM.item;

            this.$emit('input', value);
            this.$emit('update:value', value);
            this.$emit('select', {
                value,
                oldValue,
                item,
                itemVM: this.selectedVM,
            });
        },
    },
};

import Select from '../u-select.vue';

export default {
    name: 'u-select--0',
    childName: 'u-select--0-item',
    mixins: [Select],
    methods: {
        select($event) {
            const oldValue = this.value;
            const value = $event.target.value || undefined;
            this.watchValue(value);
            const item = this.selectedVM && this.selectedVM.item;

            this.$emit('input', value);
            this.$emit('update:value', value);
            this.$emit('select', {
                value,
                oldValue,
                item,
                itemVM: this.selectedVM,
            });
        },
    },
};

Fix first option not changed


function loadCreator (collectionId) {
  var pageType, releaseDate;

  getCollection(collectionId,
    success = function (response) {
      if (!response.publishDate) {
        releaseDate = null;
      } else {
        releaseDate = response.publishDate;
      }
    },
    error = function (response) {
      handleApiError(response);
    }
  );

  $('select').off().change(function () {
    pageType = $(this).val();
    //var parentUrl = localStorage.getItem("pageurl");
    var parentUrl = Florence.globalVars.pagePath;

    if (pageType === 'bulletin' || pageType === 'article') {
      $('.edition').empty();
      loadT4Creator(collectionId, releaseDate, pageType, parentUrl);
    }

    else if (pageType.match(/compendium_.+/)) {
      $('.edition').empty();
      loadT6Creator(collectionId, releaseDate, pageType, parentUrl);
    }

    else if (pageType.match(/static_.+/)) {
      $('.edition').empty();
      loadT7Creator(collectionId, releaseDate, pageType, parentUrl);
    }

    else if (pageType === 'reference_tables' || pageType === 'dataset') {
      $('.edition').empty();
      loadT8Creator(collectionId, releaseDate, pageType, parentUrl);
    }
    else if (pageType === 'release') {
      $('.edition').empty();
      loadT16Creator(collectionId, releaseDate, pageType, parentUrl);
    }
  });
}

function loadCreator (collectionId) {
  var pageType, releaseDate;

  getCollection(collectionId,
    success = function (response) {
      if (!response.publishDate) {
        releaseDate = null;
      } else {
        releaseDate = response.publishDate;
      }
    },
    error = function (response) {
      handleApiError(response);
    }
  );

  $('select').off().change(function () {
    pageType = $(this).val();
    //var parentUrl = localStorage.getItem("pageurl");
    var parentUrl = Florence.globalVars.pagePath;
    $('.edition').empty();

    if (pageType === 'bulletin' || pageType === 'article') {
      loadT4Creator(collectionId, releaseDate, pageType, parentUrl);
    }
    else if (pageType.match(/compendium_.+/)) {
      loadT6Creator(collectionId, releaseDate, pageType, parentUrl);
    }
    else if (pageType.match(/static_.+/)) {
      loadT7Creator(collectionId, releaseDate, pageType, parentUrl);
    }
    else if (pageType === 'reference_tables' || pageType === 'dataset') {
      loadT8Creator(collectionId, releaseDate, pageType, parentUrl);
    }
    else if (pageType === 'release') {
      loadT16Creator(collectionId, releaseDate, pageType, parentUrl);
    }
  });
}

Solve bug when creating new pages


$(function() {
  $("#result").hide();

  $("#search_button").on("click", function() {
    var name = $("#name").val();
    var score = $("#score").val();
    var url = "/api/endpoints/search/?name=" + name + "&score=" + score;
    $.getJSON(url, function(data) {
      $("#result").show();
      for (var i = 0; i < data.length; i++) {
        var row = $("<tr>");
        row.append($("<td>").text(data[i].name));
        row.append($("<td>").text(data[i].url));
        row.append($("<td>").text(data[i].evaluation.score));
        $("#result_body").append(row);
      }
    });
  });
});

$(function() {
  $("#result").hide();

  $("#search_button").on("click", function() {
    var name = $("#name").val();
    var score = $("#score").val();
    var url = "/api/endpoints/search/?name=" + name + "&score=" + score;
    $.getJSON(url, function(data) {
      $("#result").show();
      for (var i = 0; i < data.length; i++) {
        var endpoint = data[i];
        var row = $("<tr>");
        row.append($("<td>").append($("<a>").attr("href", "/endpoints/" + endpoint.id).text(endpoint.name)));
        row.append($("<td>").append($("<a>").attr("href", endpoint.url).text(endpoint.url)));
        row.append($("<td>").text(endpoint.evaluation.score));
        $("#result_body").append(row);
      }
    });
  });
});

Add anchors to the value of "NAME" and "URL", respectively


import React, { Component, PropTypes } from 'react';
import fetchData from '../../actions/fetchData';
import Toast from '../../components/Toast';
import Modals from '../Modals';
import SocketEvents from '../../utils/socketEvents';
import t from '../../utils/types';
import './Main.scss';

import MainNav from '../MainNav';

export default class Main extends Component {
  static propTypes = {
    site: t.site.isRequired,
    socket: PropTypes.object.isRequired,
    ui: PropTypes.object.isRequired,
    user: t.user.isRequired,
    location: PropTypes.object.isRequired,
    children: PropTypes.object.isRequired,
    dispatch: PropTypes.func.isRequired,
  };

  componentDidMount() {
    const { dispatch, socket } = this.props;

    fetchData();

    const events = new SocketEvents(socket, dispatch);
    events.listen();
  }

  render() {
    const { ui, dispatch, site, user } = this.props;

    const isFetching = Object.keys(this.props).some(key => this.props[key].isFetching);
    if (isFetching) return null;

    return (
      <main className="main">
        <MainNav siteName={site.siteName} user={user} />
        {React.cloneElement(this.props.children, {
          ...this.props,
          key: this.props.location.pathname,
        })}

        <div className="toasts">
          {ui.toasts.map(t => <Toast dispatch={dispatch} key={t.dateCreated} {...t} />)}
        </div>
        <Modals {...this.props} />
      </main>
    );
  }
}

import React, { Component, PropTypes } from 'react';
import fetchData from '../../actions/fetchData';
import Toast from '../../components/Toast';
import Modals from '../Modals';
import SocketEvents from '../../utils/socketEvents';
import t from '../../utils/types';
import './Main.scss';

import MainNav from '../MainNav';

export default class Main extends Component {
  static propTypes = {
    site: t.site,
    socket: PropTypes.object.isRequired,
    ui: PropTypes.object.isRequired,
    user: t.user,
    location: PropTypes.object.isRequired,
    children: PropTypes.object.isRequired,
    dispatch: PropTypes.func.isRequired,
  };

  static defaultProps = {
    site: null,
    user: null,
  }

  componentDidMount() {
    const { dispatch, socket } = this.props;

    fetchData();

    const events = new SocketEvents(socket, dispatch);
    events.listen();
  }

  render() {
    const { ui, dispatch, site, user } = this.props;

    const isFetching = Object.keys(this.props).some(key => this.props[key].isFetching);
    if (isFetching) return null;

    return (
      <main className="main">
        <MainNav siteName={site.siteName} user={user} />
        {React.cloneElement(this.props.children, {
          ...this.props,
          key: this.props.location.pathname,
        })}

        <div className="toasts">
          {ui.toasts.map(t => <Toast dispatch={dispatch} key={t.dateCreated} {...t} />)}
        </div>
        <Modals {...this.props} />
      </main>
    );
  }
}

Remove two more required prop warnings


(function (global) {
  'use strict';
  var $ = global.jQuery;
  var $geoLocate = $('.geo-locate');
  var $geoLocateDenied = $('.geo-locate--denied');
  var $geoLocateError = $('.geo-locate--error');
  var $geoLocateLocate = $('.geo-locate--locate');
  var $geoLocateSearching = $('.geo-locate--searching');
  var window = global;

  function success(position) {
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;

    if (latitude && longitude) {
      var locationDescription = 'your location';
      var location = './results?location=' + encodeURIComponent(locationDescription) + '&latitude=' + latitude + '&longitude=' + longitude;
      // load the results page
      window.location = location;
    } else {
      error();
    }
  }

  function error(e) {
    switch(e.code) {
      case e.PERMISSION_DENIED:
        $geoLocate.hide();
        $geoLocateDenied.show();
        break;
      case e.POSITION_UNAVAILABLE:
        $geoLocateError.show();
        break;
      case e.TIMEOUT:
        $geoLocateError.show();
        break;
    }
    $geoLocateSearching.hide();
  }

  if (navigator.geolocation) {
    $geoLocate.show();

    $geoLocateLocate.on('click', function() {
      $geoLocateError.hide();
      navigator.geolocation.getCurrentPosition(success, error);
      $geoLocateSearching.show();
    });
  }
})(window);

(function (global) {
  'use strict';
  var $ = global.jQuery;
  var $geoLocate = $('.geo-locate');
  var $geoLocateDenied = $('.geo-locate--denied');
  var $geoLocateError = $('.geo-locate--error');
  var $geoLocateLocate = $('.geo-locate--locate');
  var $geoLocateSearching = $('.geo-locate--searching');
  var window = global;

  function success(position) {
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;

    if (latitude && longitude) {
      var locationDescription = 'your location';
      var location = './results?location=' + encodeURIComponent(locationDescription) + '&latitude=' + latitude + '&longitude=' + longitude;
      // load the results page
      window.location = location;
    } else {
      error();
    }
  }

  function error(e) {
    switch(e.code) {
      case e.PERMISSION_DENIED:
        $geoLocate.hide();
        $geoLocateDenied.show();
        break;
      case e.POSITION_UNAVAILABLE:
        $geoLocateError.show();
        break;
      case e.TIMEOUT:
        $geoLocateError.show();
        break;
    }
    $geoLocateSearching.hide();
  }

  if (navigator.geolocation) {
    $geoLocate.show();

    $geoLocateLocate.on('click', function(e) {
      $geoLocateError.hide();
      navigator.geolocation.getCurrentPosition(success, error);
      $geoLocateSearching.show();
      e.preventDefault();
    });
  }
})(window);

Stop page repositioning after using my location


function AudioFile() {
	this.name = new Array();
	this.data = new Array();
	this.counter = 0;
};

AudioFile.prototype.load = function(_audioSrc, _audioName) {
	if(!this.getAudioDataByName(_audioName)) {
		var self = this;
		var _audioSrcFile;
		var _audio = new Audio();
		for(var i = 0; i < _audioSrc.length; i++) {
			var t = _audioSrc[i].split('.');
			if(_audio.canPlayType('audio/' + t[t.length - 1]) != '') {
				_audioSrcFile = _audioSrc[i];
			}
		}
		_audio.src = _audioSrcFile;
		_audio.addEventListener('canplaythrough', function(){self.counter++;});
		this.name.push(_audioName);
		this.data.push(_audio);
	}
};

AudioFile.prototype.isLoaded = function() {
	if(this.counter === this.data.length) {
		return true;
	}
	return false;
};

AudioFile.prototype.getAudioDataByName = function(_audioName) {
	return this.data[this.name.indexOf(_audioName)];
};

function AudioFile() {
	this.name = new Array();
	this.data = new Array();
	this.counter = 0;
};

AudioFile.prototype.load = function(_audioSrc, _audioName) {
	if(!this.getAudioDataByName(_audioName)) {
		var self = this;
		var _audioSrcFile;
		var _audio = new Audio();
		for(var i = 0; i < _audioSrc.length; i++) {
			var t = _audioSrc[i].split('.');
			if(_audio.canPlayType('audio/' + t[t.length - 1]) != '') {
				_audioSrcFile = _audioSrc[i];
			}
		}
		_audio.src = _audioSrcFile;
		_audio.addEventListener('canplay', function(){self.counter++;});
		this.name.push(_audioName);
		this.data.push(_audio);
	}
};

AudioFile.prototype.isLoaded = function() {
	if(this.counter === this.data.length) {
		return true;
	}
	return false;
};

AudioFile.prototype.getAudioDataByName = function(_audioName) {
	return this.data[this.name.indexOf(_audioName)];
};

Change event canplay to load audio


/* global Module, Log, MM, config */

/* Magic Mirror
 * Module: Manager Module
 *
 * By Rodrigo Ramìrez Norambuena https://rodrigoramirez.com
 * MIT Licensed.
 */

Module.register("MagicMirror-Module-Manager", {

	// Set the minimum MagicMirror module version.
	requiresVersion: "2.1.0",

	// Default module config.
	defaults: {
		baseUrlApi: 'http://beta.magicmirror.builders/api',
		limit: 20
	},

	// Define start sequence.
	start: function() {
		Log.info("Starting module: " + this.name);
	},

	getStyles: function() {
		return ["module-manager.css"];
	},

});

/* global Module, Log, MM, config */

/* Magic Mirror
 * Module: Manager Module
 *
 * By Rodrigo Ramìrez Norambuena https://rodrigoramirez.com
 * MIT Licensed.
 */

Module.register("MagicMirror-Module-Manager", {

	// Set the minimum MagicMirror module version.
	requiresVersion: "2.1.0",

	// Default module config.
	defaults: {
		baseUrlApi: 'http://beta.magicmirror.builders/api',
		limit: 20
	},

	// Define start sequence.
	start: function() {
		Log.info("Starting module: " + this.name);
	},

	getStyles: function() {
		return ["module-manager.css"];
	},

	getModule: function(name) {
		return MM.getModules().withClass(name)[0];
	},

	getConfigModule: function(name) {
		return this.getModule(name).config;
	},

	getAllModules: function() {
		return MM.getModules();
	}

});

Add functions for get modules and configs


#!/usr/bin/env node
'use strict';

require('shelljs/global');
set('-e');

if (process.env.JOB === 'e2e-guru') {
  exec('npm run e2e');
} else {
  exec('npm run unit');
  console.log('Starting Basic E2E');
  exec('npm run e2e');
}

#!/usr/bin/env node
'use strict';

require('shelljs/global');
set('-e');

function isPR() {
  return process.env.TRAVIS_PULL_REQUEST && process.env.TRAVIS_PULL_REQUEST !== 'false';
}

if (process.env.JOB === 'e2e-guru') {
  if (isPR()) {
    console.log('Skiping E2E tests on PR');
    return;
  }
  exec('npm run e2e');
} else {
  exec('npm run unit');
  if (isPR()) {
    console.log('Skiping E2E tests on PR');
    return;
  }
  console.log('Starting Basic E2E');
  exec('npm run e2e');
}

Fix tests failure on PR


'use strict';

module.exports =
{
	base_name		: 'keywords',
	action_name		: 'keywords/{id}',

	create: function() {
		return this.action(...arguments);
	},
};

'use strict';

module.exports =
{
	base_name		: 'keywords',
	action_name		: 'keywords/{id}',

	create: function() {
		return this.action.apply(this, arguments);
	},
};

Add Node <5 support by removing spread operator


//
// suuoh.com
// Melvin Chien 2013
//

$(document).ready(function() {
  $("a[href^='#']").click(function(){
    var id = $(this).attr("href");
    var posTop = $(id).position().top;
    if ($(".hidden-phone").is(":visible"))
      posTop -= 50;
    $("html, body").animate({
      scrollTop: posTop
    }, 1000);
  });
  $("a[href^='http']").attr("target", "_blank");
  $("a[class='tooltip-hover']").tooltip();
  $("img.lazy").show().lazyload({
    effect: "fadeIn"
  });
  $("a[href$='.gif'],a[href$='.jpg'],a[href$='.png']").fancybox({
    padding: 10,
    margin: 50,
    loop: false,
    helpers: {
      title: {
        type: "over"
      },
      overlay: {
        showEarly: false,
        css: {
          "background" : "rgba(25, 25, 25, 0.80)"
        }
      }
    }
  });
  $(".fancybox-media").fancybox({
    padding: 10,
    margin: 50,
    loop: false,
    helpers: {
      media: {},
      title: {
        type: "float"
      },
      overlay: {
        showEarly: false,
        css: {
          "background" : "rgba(25, 25, 25, 0.80)"
        }
      }
    }
  });
});

//
// suuoh.com
// Melvin Chien 2013
//

$(document).ready(function() {
  $("a[href^='#']").click(function(e){
    e.preventDefault();
    var id = $(this).attr("href");
    var posTop = $(id).position().top;
    if ($(".hidden-phone").is(":visible"))
      posTop -= 50;
    $("html, body").animate({
      scrollTop: posTop
    }, 1000);
  });
  $("a[href^='http']").attr("target", "_blank");
  $("a[class='tooltip-hover']").tooltip();
  $("img.lazy").show().lazyload({
    effect: "fadeIn"
  });
  $("a[href$='.gif'],a[href$='.jpg'],a[href$='.png']").fancybox({
    padding: 10,
    margin: 50,
    loop: false,
    helpers: {
      title: {
        type: "over"
      },
      overlay: {
        showEarly: false,
        css: {
          "background" : "rgba(25, 25, 25, 0.80)"
        }
      }
    }
  });
  $(".fancybox-media").fancybox({
    padding: 10,
    margin: 50,
    loop: false,
    helpers: {
      media: {},
      title: {
        type: "float"
      },
      overlay: {
        showEarly: false,
        css: {
          "background" : "rgba(25, 25, 25, 0.80)"
        }
      }
    }
  });
});

Remove default anchor behavior for smooth scrolling.


const _ = require('lodash');

exports.processHeaders = function processHeaders(headerParams, options) {
  if (headerParams) {
    const customHeaders = _.isArray(headerParams) ? headerParams : [ headerParams ];
    customHeaders.forEach(header => {
      const [key, value] = header.split(':').map(str => str.trim());
      options.headers[key] = value;
    });
  }
}

const _ = require('lodash');

exports.processHeaders = function processHeaders(headerParams, options) {
  if (headerParams) {
    const customHeaders = _.isArray(headerParams) ? headerParams : [ headerParams ];
    customHeaders.forEach(header => {
      const keyValuePair = header.split(':').map(str => str.trim());
      options.headers[keyValuePair[0]] = keyValuePair[1];
    });
  }
}

Remove destructuring for node 4 compatibility


import React, { Component } from 'react';
import { connect } from 'react-redux';
import '../scss/_profile.scss';
import RecipeContainer from './RecipeContainer.js';

class Profile extends Component {

  componentDidMount() {

  }

  componentWillUpdate(nextProps) {

  }

  render() {
    const { avatar, user, recipesOwned, recipesFollowed, recipesLiked } = this.props;
    return (

{user.displayName}

          <RecipeContainer
            className="recipes-owned"
            type="My Recipes"
            recipes={recipesOwned}
          />
            <RecipeContainer
            className="recipes-followed"
            type="Followed Recipes"
            recipes={recipesFollowed}
          />
          <RecipeContainer
            className="recipes-liked"
            type="Liked Recipes"
            recipes={recipesLiked}
          />

    );
  }
}

const mapStateToProps = (state) => {
  return {
    user: state.user,
    avatar: state.user.photos[0].value,
    recipesOwned: state.recipesOwned,
    recipesFollowed: state.recipesFollowed,
    recipesLiked: state.recipesLiked,
  };
};

export default connect(mapStateToProps)(Profile);

import React, { Component } from 'react';
import { connect } from 'react-redux';
import '../scss/_profile.scss';
import RecipeContainer from './RecipeContainer.js';

class Profile extends Component {

  componentDidMount() {

  }

  componentWillUpdate(nextProps) {

  }

  render() {
    const { avatar, user, recipesOwned, recipesFollowed, recipesLiked } = this.props;
    const avatarLarge = avatar.slice(0, avatar.length-6);
    return (

{user.displayName}

          <RecipeContainer
            className="recipes-owned"
            type="My Recipes"
            recipes={recipesOwned}
          />
            <RecipeContainer
            className="recipes-followed"
            type="Followed Recipes"
            recipes={recipesFollowed}
          />
          <RecipeContainer
            className="recipes-liked"
            type="Liked Recipes"
            recipes={recipesLiked}
          />

    );
  }
}

const mapStateToProps = (state) => {
  return {
    user: state.user,
    avatar: state.user.photos[0].value,
    recipesOwned: state.recipesOwned,
    recipesFollowed: state.recipesFollowed,
    recipesLiked: state.recipesLiked,
  };
};

export default connect(mapStateToProps)(Profile);

Use large avatar on profile page


import React from 'react'
import classnames from 'classnames'
import { Link } from 'react-router'
import classes from './Users.scss'
import { userPoolId } from 'utils/settings'

export default class Users extends React.Component {
  render () {
    const region = userPoolId.match(/^[a-z0-9-]+/)
    if (!region) {
      console.warn('user pool id has unexpected format:', region)
    }
    const linkToUsersPage = `https://${region}.console.aws.amazon.com/cognito/users?region=${region}#/pool/${userPoolId}/users`
    return (

Users

        Operations like creating a user are not supported here. Access
        <Link to={linkToUsersPage} className={classes.link}>
          Users page at Amazon Cognito
        </Link>
        for these operations.

)
  }
}

import React from 'react'
import classnames from 'classnames'
import classes from './Users.scss'
import { userPoolId } from 'utils/settings'

export default class Users extends React.Component {
  render () {
    const region = userPoolId.match(/^[a-z0-9-]+/)
    if (!region) {
      console.warn('user pool id has unexpected format:', region)
    }
    const linkToUsersPage = `https://${region}.console.aws.amazon.com/cognito/users?region=${region}#/pool/${userPoolId}/users`
    return (

Users

        Operations like creating a user are not supported here. Access

          Users page at Amazon Cognito

        for these operations.

)
  }
}

Fix the invalid use of Link component


'use strict';

var fadeMenuDirective = angular.module('ngFadeMenu', [ ] );

fadeMenuDirective.directive( 'ngFadeMenu', function( $timeout, screenSize )
{
	return {
		restrict: 'A',
		link: function( scope, element, attr )
		{
			scope.mobile = screenSize.on( 'xs, sm', function( match ){
				scope.mobile = match;
			});

			scope.$watch( 'mobile' );

			if ( !scope.mobile )
			{
				var timeoutPromise;
				// resetFadeTimeout();
				element.parent(  ).parent(  ).parent(  ).parent(  ).parent(  ).bind( 'mousemove', function( e )
				{
					resetFadeTimeout(  );
				} );

				scope.$watch( 'stateName', function( newValue, oldValue )
				{
					resetFadeTimeout(  );
				} );

				scope.$watch( 'stateParams.section', function( newValue, oldValue )
				{
					resetFadeTimeout(  );
				} );

				function resetFadeTimeout(  )
				{
					element.removeClass( 'hidden' );
					$timeout.cancel( timeoutPromise );
					if ( scope.stateName === "root" || ( scope.stateName === "root.section-state" && scope.stateParams.section !== "news" && scope.stateParams.section !== "search" ) )
					{
						timeoutPromise = $timeout( function ( e )
						{
							element.addClass( 'hidden' );
						}, 3000 );
					}
				}
			}

		}
	}
});

'use strict';

var fadeMenuDirective = angular.module('ngFadeMenu', [ ] );

fadeMenuDirective.directive( 'ngFadeMenu', function( $timeout, screenSize )
{
	return {
		restrict: 'A',
		link: function( scope, element, attr )
		{
			scope.mobile = screenSize.on( 'xs, sm', function( match ){
				scope.mobile = match;
			});

			var timeoutPromise;
			// resetFadeTimeout();
			element.parent(  ).parent(  ).parent(  ).parent(  ).parent(  ).bind( 'mousemove', function( e )
			{
				resetFadeTimeout(  );
			} );

			scope.$watch( 'stateName', function( newValue, oldValue )
			{
				resetFadeTimeout(  );
			} );

			scope.$watch( 'stateParams.section', function( newValue, oldValue )
			{
				resetFadeTimeout(  );
			} );

			function resetFadeTimeout(  )
			{
				if( !scope.mobile )
				{
					element.removeClass( 'hidden' );
					$timeout.cancel( timeoutPromise );
					if ( scope.stateName === "root" || ( scope.stateName === "root.section-state" && scope.stateParams.section !== "news" && scope.stateParams.section !== "search" ) )
					{
						timeoutPromise = $timeout( function ( e )
						{
							element.addClass( 'hidden' );
						}, 3000 );
					}
				}
			}
		}
	}
});

Fix menu fade mobile detection iOS bug


var path = require('path');

var createPattern = function(path, included) {
  return {pattern: path, included: included, served: true, watched: false};
};

var initMocha = function(files, mochaConfig) {
  var mochaPath = path.dirname(require.resolve('mocha'));
  files.unshift(createPattern(__dirname + '/testrunner.sw.js'));
  files.unshift(createPattern(__dirname + '/index.html'));
  files.unshift(createPattern(mochaPath + '/mocha.js'));
  files.unshift(createPattern(__dirname + '/adapter.js', true));

  if (mochaConfig && mochaConfig.reporter) {
    files.unshift(createPattern(mochaPath + '/mocha.css'));
  }
};

initMocha.$inject = ['config.files', 'config.client.mocha'];

module.exports = {
  'framework:mocha': ['factory', initMocha]
};

var path = require('path');

var createPattern = function(path, included) {
  return {pattern: path, included: included, served: true, watched: false};
};

var initMocha = function(files, mochaConfig) {
  var mochaPath = path.dirname(require.resolve('mocha'));
  files.unshift(createPattern(__dirname + '/testrunner.sw.js'));
  files.unshift(createPattern(__dirname + '/index.html'));
  files.unshift(createPattern(mochaPath + '/mocha.js'));
  files.unshift(createPattern(__dirname + '/adapter.js', true));

  if (mochaConfig && mochaConfig.reporter) {
    files.unshift(createPattern(mochaPath + '/mocha.css'));
  }
};

initMocha.$inject = ['config.files', 'config.client.mocha'];

module.exports = {
  'framework:sw-mocha': ['factory', initMocha]
};

Change the name for the adapter


"use strict";

var use = require("alamid-plugin/use.js");

var path = require("path"),
    argv = require("minimist")(process.argv.slice(2));

function dynamicConfig(basePath, fileName) {

    var env = dynamicConfig.getEnv(),
        filePath = dynamicConfig.getFilePath(basePath, env, fileName),
        config;

    if (dynamicConfig.options.log) {
        console.log("Loading configuration '" + fileName + "'  from path '" + filePath + "'");
    }

    config = dynamicConfig.loadConfig(filePath);

    return config;
}

dynamicConfig.getEnv = function() {
    return process.env.env || argv.env || argv.ENV || dynamicConfig.options.defaultEnv;
};

dynamicConfig.getFilePath = function(basePath, env, fileName) {
    return path.join(basePath, env, fileName);
};

dynamicConfig.loadConfig = function(filePath) {
    var config;

    try {
        config = require(filePath);
    }
    catch (err) {

        //handle only not found errors, throw the rest
        if (err.code === "MODULE_NOT_FOUND") {
            throw new Error("Config not found at '" + filePath + "'");
        }

        throw err;
    }

    return config;
};

dynamicConfig.options = {
    defaultEnv: "develop",
    log: false
};

dynamicConfig.use = use;

module.exports = dynamicConfig;

"use strict";

var use = require("alamid-plugin/use.js");

var path = require("path"),
    argv = require("minimist")(process.argv.slice(2));

function dynamicConfig(basePath, fileName) {

    var env = dynamicConfig.getEnv(),
        filePath = dynamicConfig.getFilePath(basePath, env, fileName),
        config;

    if (dynamicConfig.options.log) {
        console.log("Loading configuration '" + fileName + "'  from path '" + filePath + "'");
    }

    config = dynamicConfig.loadConfig(filePath);

    return config;
}

dynamicConfig.getEnv = function() {
    return argv.env || argv.ENV || process.env.env || dynamicConfig.options.defaultEnv;
};

dynamicConfig.getFilePath = function(basePath, env, fileName) {
    return path.join(basePath, env, fileName);
};

dynamicConfig.loadConfig = function(filePath) {
    var config;

    try {
        config = require(filePath);
    }
    catch (err) {

        //handle only not found errors, throw the rest
        if (err.code === "MODULE_NOT_FOUND") {
            throw new Error("Config not found at '" + filePath + "'");
        }

        throw err;
    }

    return config;
};

dynamicConfig.options = {
    defaultEnv: "develop",
    log: false
};

dynamicConfig.use = use;

module.exports = dynamicConfig;

Change order of env sources in getEnv()


'use strict';

var async = require('async');
var Anyfetch = require('anyfetch');

/**
 * HYDRATING FUNCTION
 *
 * @param {string} path Path of the specified file
 * @param {string} original document
 * @param {object} changes object provided by anyFetch's API. Update this object to send document's modification to anyFetch's API.
 * @param {function} cb Callback, first parameter is the error if any, then the processed data
 */
module.exports = function(path, document, changes, cb) {
  var anyfetch = new Anyfetch(document.access_token);
  anyfetch.setApiUrl(cb.apiUrl);

  async.waterfall([
    function(cb) {
      cb();
    },
  ], function(err) {
    cb(err, changes);
  });
};

'use strict';

var async = require('async');
var crypto = require('crypto');
var Anyfetch = require('anyfetch');

/**
 * HYDRATING FUNCTION
 *
 * @param {string} path Path of the specified file
 * @param {string} original document
 * @param {object} changes object provided by anyFetch's API. Update this object to send document's modification to anyFetch's API.
 * @param {function} cb Callback, first parameter is the error if any, then the processed data
 */
module.exports = function(path, document, changes, cb) {
  var anyfetch = new Anyfetch(document.access_token);
  anyfetch.setApiUrl(cb.apiUrl);

  async.waterfall([
    function generateHash(cb) {
      var shasum = crypto.createHash('sha1');
      shasum.update(JSON.stringify(document.metadata));
      cb(null, shasum.digest('hex'));
    },
    function getDocumentsWithSameHash(hash, cb) {
      changes.hash = hash;

      anyfetch.getDocuments({hash: hash}, cb);
    },
    function deleteOldDocuments(res, cb) {
      var documents = res.body.data;

      async.each(documents, function(document, cb) {
        console.log("DELETE", document.identifier);
        anyfetch.deleteDocumentById(document.id, cb);
      }, cb);
    }
  ], function(err) {
    cb(err, changes);
  });
};

Delete documents with same hash and update hash


'use strict';

var cp  = require('child_process');
var log = console.log;

module.exports = function () {

    module.constructor.prototype.require = function (path) {

        var self = this;

        try {
            return self.constructor._load(path, self);
        } catch (e) {
            if (e.code !== 'MODULE_NOT_FOUND') {
             // Bail out, something went seriously wrong here!
                throw e;
            }
            // Trying to import local file, not much we can help with here!
            else if (path.charAt(0) === '.' || path.charAt(0) === '/' ) {
                log('Local module at path ' + path + ' was not found!')
                throw e;
            }

            else {
                log('Looks like module ' + path  + ' was not found! Installing...');
                var results = cp.execSync('npm i ' + path, {stdio : [0, 1, 2]})
                if (results && results.err) {
                    throw results.err;
                } else {
                    return self.constructor._load(path, self);
                }
            }
        }
    }
}

'use strict';

var cp  = require('child_process');
var log = console.log;

module.exports = function (npmOptions) {

    if (typeof npmOptions != 'string' && npmOptions.constructor !== Array) {
        throw new Error('Parameter must be an array or a single string!')
    }
    npmOptions = [].concat(npmOptions);

    module.constructor.prototype.require = function (path) {

        var self = this;

        try {
            return self.constructor._load(path, self);
        } catch (e) {
            if (e.code !== 'MODULE_NOT_FOUND') {
             // Bail out, something went seriously wrong here!
                throw e;
            }
            // Trying to import local file, not much we can help with here!
            else if (path.charAt(0) === '.' || path.charAt(0) === '/' ) {
                log('Local module at path ' + path + ' was not found!')
                throw e;
            }

            else {
                log('Looks like module ' + path  + ' was not found! Installing...');
                var cmdParameters = npmOptions.join(' ');

                console.log(cmdParameters);
                var results = cp.execSync('npm i ' + cmdParameters + ' ' + path, {stdio : [0, 1, 2]})
                if (results && results.err) {
                    throw results.err;
                } else {
                    return self.constructor._load(path, self);
                }
            }
        }
    }
}

Add support for command line parameters


var knox = require('knox'),
  _ = require('underscore');

function Saver(opts) {
	_.extend(this, {
		aws_key: process.env.AWS_KEY,
		aws_secret: process.env.AWS_SECRET,
		bucket: process.env.BUCKET
	}, opts);

	if (this.s3) return;

	this.s3 = knox.createClient({
		key: this.aws_key,
		secret: this.aws_secret,
		bucket: this.bucket
	});
}

Saver.prototype.save = function(source, destination, callback) {
	this.s3.putFile(source, destination, { 'x-amz-acl': 'public-read' }, function(err, res){

		if (err) {
			callback(err);
			return;
		}

		console.log('saved ' + source + ' to ' + destination)
		callback();
	});
};

exports.Saver = Saver;

var knox = require('knox'),
  _ = require('underscore');

function Saver(opts) {
	_.extend(this, {
		aws_key: process.env.AWS_KEY,
		aws_secret: process.env.AWS_SECRET,
		bucket: process.env.BUCKET
	}, opts);

	if (this.s3) return;

	this.s3 = knox.createClient({
		key: this.aws_key,
		secret: this.aws_secret,
		bucket: this.bucket
	});
}

Saver.prototype.save = function(source, destination, callback) {
	this.s3.putFile(source, destination, function(err, res){

		if (err) {
			callback(err);
			return;
		}

		console.log('saved ' + source + ' to ' + destination)
		callback();
	});
};

exports.Saver = Saver;

Revert "* Change ACL to be public by default"


import {
  isUndefined,
  isObject,
  isEmpty
} from 'lodash';

import {
  NoListenToGiven,
  NoInitialStateGiven
} from './errors';

export default class Reducer {
  constructor(options) {
    this.initialState = options.initialState;
    this.listenTo = options.listenTo;

    if (isUndefined(this.initialState)) {
      throw new NoInitialStateGiven();
    }

    if (!isObject(this.listenTo) || isEmpty(this.listenTo)) {
      throw new NoListenToGiven();
    }
  }

  build() {
    return (state = this.initialState, action) => (
      this.hasHandler(action) ?
      this.listenTo[action.type].call({ state, action }, state, action) :
      state
    );
  }

  hasHandler(action) {
    return this.listenTo.hasOwnProperty(action.type);
  }
}

import {
  isUndefined,
  isObject,
  isEmpty
} from 'lodash';

import {
  NoListenToGiven,
  NoInitialStateGiven
} from './errors';

export default class Reducer {
  constructor(options) {
    this.initialState = options.initialState;
    this.listenTo = options.listenTo;

    if (isUndefined(this.initialState)) {
      throw new NoInitialStateGiven();
    }

    if (!isObject(this.listenTo) || isEmpty(this.listenTo)) {
      throw new NoListenToGiven();
    }
  }

  build() {
    return (state = this.initialState, action) => (
      this.hasHandler(action) ?
      this.listenTo[action.type].call({ state, action }, state, action) :
      state
    );
  }

  hasHandler(action) {
    return isObject(action) &&
      this.listenTo.hasOwnProperty(action.type);
  }
}

Make sure action is object


import {
  REGISTER_LIST,
  DESTROY_LIST,

  LOAD_LIST,
  LOAD_LIST_SUCCESS,
  LOAD_LIST_ERROR,
} from './actionsTypes'

export function registerList(listId, params) {
  return {
    type: REGISTER_LIST,
    payload: {
      listId,
      params,
    },
  }
}

export function destroyList(listId) {
  return {
    type: DESTROY_LIST,
    payload: {
      listId,
    },
  }
}

export function loadList(listId) {
  return {
    type: LOAD_LIST,
    payload: {
      listId,
    },
  }
}

export function loadListSuccess(listId, response) {
  return {
    type: LOAD_LIST_SUCCESS,
    payload: {
      listId,
      response,
    },
  }
}

export function loadListError(listId, response) {
  return {
    type: LOAD_LIST_ERROR,
    payload: {
      listId,
      response,
    },
  }
}

import {
  REGISTER_LIST,
  DESTROY_LIST,

  LOAD_LIST,
  LOAD_LIST_SUCCESS,
  LOAD_LIST_ERROR,
} from './actionsTypes'

export function registerList(listId, params) {
  return {
    type: REGISTER_LIST,
    payload: {
      listId,
      params,
    },
  }
}

export function destroyList(listId) {
  return {
    type: DESTROY_LIST,
    payload: {
      listId,
    },
  }
}

export function loadList(listId) {
  return {
    type: LOAD_LIST,
    payload: {
      listId,
    },
  }
}

export function loadListSuccess(listId, response) {
  if (!response) {
    throw new Error('Response is required')
  }

  if (!response.items) {
    throw new Error('Response items is required')
  }

  if (!(response.items instanceof Array)) {
    throw new Error('Response items should be array')
  }

  return {
    type: LOAD_LIST_SUCCESS,
    payload: {
      listId,
      response,
    },
  }
}

export function loadListError(listId, response) {
  return {
    type: LOAD_LIST_ERROR,
    payload: {
      listId,
      response,
    },
  }
}

Validate response in loadListSuccess action and throw error if invalid


const Discord = require('discord.js');
const { token } = require('../credentials.json');
const CommandEngine = require('./commandEngine.js');
const logger = require('./logger.js');
const { getGuildString } = require('./localeEngine.js');

const Client = new Discord.Client()

Client.on('ready', () => logger.log('Client ready.'));

Client.on('message', async (message) => {
  const commandMap = CommandEngine.getCommandMap(message.guild);
  const entries = [...commandMap.entries()];
  const result = entries.find(([regExp]) =>
    message.content.search(regExp) != -1
  );
  if (result === undefined)
    return;
  const [regExp, command] = result;
  const [, variant, arg] = regExp.exec(message);
  const hasPerms = CommandEngine.checkPermissions(message, command);
  if (!hasPerms) {
    message.channel.send(l(message.guild)('no_permissions'));
    return;
  }
  command.action(message, arg, variant);
  logger.command(message);
});

(async () => {
  try {
    await Client.login(token);
    logger.log('Logged in successfully.')
  } catch (err) {
    logger.error('Cannot log in, error:\n' + error);
    process.exit(0);
  }
})();

const Discord = require('discord.js');
const { token } = require('../credentials.json');
const CommandEngine = require('./commandEngine.js');
const logger = require('./logger.js');
const { getGuildString } = require('./localeEngine.js');

const Client = new Discord.Client()

Client.on('ready', () => logger.log('Client ready.'));

Client.on('message', async (message) => {
  const commandMap = CommandEngine.getCommandMap(message.guild);
  const entries = [...commandMap.entries()];
  const result = entries.find(([regExp]) =>
    message.content.search(regExp) != -1
  );
  if (result === undefined)
    return;
  const [regExp, command] = result;
  const [, variant, arg] = regExp.exec(message);
  const hasPerms = CommandEngine.checkPermissions(message, command);
  if (!hasPerms) {
    message.channel.send(l(message.guild)('no_permissions'));
    return;
  }
  logger.command(message);
  command.action(message, arg, variant);
});

(async () => {
  try {
    await Client.login(token);
    logger.log('Logged in successfully.')
  } catch (err) {
    logger.error('Cannot log in, error:\n' + error);
    process.exit(0);
  }
})();

Swap command logging and action


'use strict';

var Game = require( './game' );
var Color = require( './math/color' );
var Geometry = require( './geometry/geometry' );
var Material = require( './materials/material' );
var LambertMaterial = require( './materials/lambert-material' );
var LambertGlowMaterial = require( './materials/lambert-glow-material' );
var Mesh = require( './objects/mesh' );
var DirectionalLight = require( './lights/directional-light' );

var game = new Game(
  Math.min( window.innerWidth, 568 ),
  Math.min( window.innerHeight, 320 )
);

document.body.appendChild( game.canvas );

var scene = game.scene;

'use strict';

var Game = require( './game' );
var Color = require( './math/color' );
var Geometry = require( './geometry/geometry' );
var Material = require( './materials/material' );
var LambertMaterial = require( './materials/lambert-material' );
var LambertGlowMaterial = require( './materials/lambert-glow-material' );
var Mesh = require( './objects/mesh' );
var DirectionalLight = require( './lights/directional-light' );

var $ = document.querySelector.bind( document );

function on( el, type, listener ) {
  el.addEventListener( type, listener );
}

function off( el, type, listener ) {
  el.removeEventListener( type, listener );
}

function create( type ) {
  return document.createElement( type );
}

function append( parent, el ) {
  parent.appendChild( el );
}

function prepend( parent, el ) {
  parent.insertBefore( el, parent.firstChild );
}

function remove( el ) {
  if ( el.parentNode ) {
    el.parentNode.removeChild( el );
  }
}

function textContent( el, text ) {
  el.textContent = text;
}

function addClass( el, className ) {
  el.classList.add( className );
}

function removeClass( el, className ) {
  el.classList.remove( className );
}

var game = new Game(
  Math.min( window.innerWidth, 568 ),
  Math.min( window.innerHeight, 320 )
);

document.body.appendChild( game.canvas );

var scene = game.scene;

Add DOM manipulation utility functions.


'use strict'
/* eslint-env browser, webextensions */

const browser = require('webextension-polyfill')
const html = require('choo/html')
const navItem = require('./nav-item')

module.exports = function contextActions ({
  isIpfsContext,
  isPinning,
  isUnPinning,
  isPinned,
  isIpfsOnline,
  onCopyIpfsAddr,
  onCopyPublicGwAddr,
  onPin,
  onUnPin
}) {
  if (!isIpfsContext) return null

  return html`
    <div class="bb b--black-20 mv2 pb2">
      ${navItem({
        text: browser.i18n.getMessage('panelCopy_currentIpfsAddress'),
        onClick: onCopyIpfsAddr
      })}
      ${navItem({
        text: browser.i18n.getMessage('panel_copyCurrentPublicGwUrl'),
        onClick: onCopyPublicGwAddr
      })}
      ${isIpfsOnline && isPinned ? null : (
        navItem({
          text: browser.i18n.getMessage('panel_pinCurrentIpfsAddress'),
          disabled: isPinning,
          onClick: onPin
        })
      )}
      ${isIpfsOnline && isPinned ? (
        navItem({
          text: browser.i18n.getMessage('panel_unpinCurrentIpfsAddress'),
          disabled: isUnPinning,
          onClick: onUnPin
        })
      ) : null}
    </div>
  `
}

'use strict'
/* eslint-env browser, webextensions */

const browser = require('webextension-polyfill')
const html = require('choo/html')
const navItem = require('./nav-item')

module.exports = function contextActions ({
  ipfsNodeType,
  isIpfsContext,
  isPinning,
  isUnPinning,
  isPinned,
  isIpfsOnline,
  onCopyIpfsAddr,
  onCopyPublicGwAddr,
  onPin,
  onUnPin
}) {
  if (!isIpfsContext) return null
  const isPinningSupported = (ipfsNodeType !== 'embedded')

  return html`
    <div class="bb b--black-20 mv2 pb2">
      ${navItem({
        text: browser.i18n.getMessage('panelCopy_currentIpfsAddress'),
        onClick: onCopyIpfsAddr
      })}
      ${navItem({
        text: browser.i18n.getMessage('panel_copyCurrentPublicGwUrl'),
        onClick: onCopyPublicGwAddr
      })}
      ${isIpfsOnline && isPinningSupported && !isPinned ? (
        navItem({
          text: browser.i18n.getMessage('panel_pinCurrentIpfsAddress'),
          disabled: isPinning,
          onClick: onPin
        })
      ) : null}
      ${isIpfsOnline && isPinningSupported && isPinned ? (
        navItem({
          text: browser.i18n.getMessage('panel_unpinCurrentIpfsAddress'),
          disabled: isUnPinning,
          onClick: onUnPin
        })
      ) : null}
    </div>
  `
}

Hide pinning controls when embedded js-ipfs is active


(function(Perseus) {

var InputInteger = Perseus.Widget.extend({
    initialize: function() {
        this.$input = $("<input type='number'>");
    },

    render: function() {
        this.$el.empty();
        this.$el.append(this.$input);
        return $.when(this);
    },

    focus: function() {
        this.$input.focus();
    },

    toJSON: function() {
        return {
            value: this.$input.val()
        };
    },

    set: function(options) {
        this.$input.val(options.value);
    },

    simpleValidate: function(rubric) {
        return InputInteger.validate(this.toJSON(), rubric);
    }
});

_.extend(InputInteger, {
    validate: function(state, rubric) {
        // For now, rubric is just like {value: "17"}
        // TODO(alpert): Smarter checking

        if (state.value === "") {
            return {
                type: "invalid",
                message: null
            };
        } else if (state.value === rubric.value) {
            return {
                type: "points",
                earned: 1,
                total: 1,
                message: null
            };
        } else {
            return {
                type: "points",
                earned: 0,
                total: 1,
                message: "Consider writing in the right answer, silly."
            };
        }
    }
});

// TODO(alpert): Editor

Perseus.Widgets.register("input-integer", InputInteger);

})(Perseus);

(function(Perseus) {

var InputInteger = Perseus.Widget.extend({
    initialize: function() {
        if (window.Modernizr && Modernizr.touch) {
            this.$input = $("<input type='number'>");
        } else {
            this.$input = $("<input type='text'>");
        }
    },

    render: function() {
        this.$el.empty();
        this.$el.append(this.$input);
        return $.when(this);
    },

    focus: function() {
        this.$input.focus();
    },

    toJSON: function() {
        return {
            value: this.$input.val()
        };
    },

    set: function(options) {
        this.$input.val(options.value);
    },

    simpleValidate: function(rubric) {
        return InputInteger.validate(this.toJSON(), rubric);
    }
});

_.extend(InputInteger, {
    validate: function(state, rubric) {
        // For now, rubric is just like {value: "17"}
        // TODO(alpert): Smarter checking

        if (state.value === "") {
            return {
                type: "invalid",
                message: null
            };
        } else if (state.value === rubric.value) {
            return {
                type: "points",
                earned: 1,
                total: 1,
                message: null
            };
        } else {
            return {
                type: "points",
                earned: 0,
                total: 1,
                message: "Consider writing in the right answer, silly."
            };
        }
    }
});

// TODO(alpert): Editor

Perseus.Widgets.register("input-integer", InputInteger);

})(Perseus);

Use 'text' input on non-touch devices


#!/usr/bin/env node
var cli = require('eslint/lib/cli');

var path = require('path');
var eslintConfig = path.join(__dirname, '..', 'index.js');

try {
  var argv = process.argv;
  argv.push('--config=' + eslintConfig);
  cli.execute(argv);
} catch (ex) {
  console.error(ex.message);
  console.error(ex.stack);
}

#!/usr/bin/env node
var cli = require('eslint/lib/cli');

var path = require('path');
var eslintConfig = path.join(__dirname, '..', 'index.js');

var argv = process.argv;
argv.push('--config=' + eslintConfig);
cli.execute(argv);

Make eslint fail on error.


var Registry = require('./registry.js').registry();

var F_IN_CHILD_PROCESS = false;

module.exports.inChildProcess = function(set){

	if(set === undefined){
		return F_IN_CHILD_PROCESS;
	}
	else{

		F_IN_CHILD_PROCESS = set;

		if(F_IN_CHILD_PROCESS){

			Registry = require('./registry_child_process.js').registry();

		}
	}

};

module.exports.spawn = function(actor, receive){

	Registry.register(actor, receive);

};

module.exports.send = function(){

	var args = Array.prototype.slice.call(arguments);

	Registry.deliverMessage.apply(Registry, args);

};

var Registry = require('./registry.js').registry();

//var F_IN_CHILD_PROCESS = false;
//
//module.exports.inChildProcess = function(set){
//
//	if(set === undefined){
//		return F_IN_CHILD_PROCESS;
//	}
//	else{
//
//		F_IN_CHILD_PROCESS = set;
//
//		if(F_IN_CHILD_PROCESS){
//
//			Registry = require('./registry_child_process.js').registry();
//
//		}
//	}
//
//};

module.exports.spawn = function(actor, receive){

	Registry.register(actor, receive);

};

module.exports.send = function(){

	var args = Array.prototype.slice.call(arguments);

	Registry.deliverMessage.apply(Registry, args);

};

Purge of innecesary code for inChildProcess


var MODULES_PATH = 'http://modules.seajs.org/'
var isLocal = location.href.indexOf('/~lifesinger/') > 0

if (isLocal) {
  MODULES_PATH = 'http://' + location.host + '/~lifesinger/seajs/spm/modules/'
}

seajs.config({
  base: MODULES_PATH,

  alias: {
    'jquery': '1.7.2'
  }
});

define(function(require) {

  var test = require('../../test')
  var $ = require('jquery')

  $(document).ready(function() {

    var passed = false
    var type = 'error'

    var onreadyTime = new Date().getTime()

    // no image cache
    if (onloadTime === 0) {
      passed = true
      type = 'no image cache'
    }
    // image cache case
    else if (Math.abs(onreadyTime - onloadTime) < 2000) {
      passed = true
      type = 'has image cache'
    }

    test.assert(passed, type + ' diff = ' + Math.abs(onreadyTime - onloadTime))
    test.done()

  })

})

define(function(require) {

  var test = require('../../test')
  var $ = require('../../third-party').jQuery

  $(document).ready(function() {

    var passed = false
    var type = 'error'

    var onreadyTime = new Date().getTime()

    // no image cache
    if (onloadTime === 0) {
      passed = true
      type = 'no image cache'
    }
    // image cache case
    else if (Math.abs(onreadyTime - onloadTime) < 2000) {
      passed = true
      type = 'has image cache'
    }

    test.assert(passed, type + ' diff = ' + Math.abs(onreadyTime - onloadTime))
    test.done()

  })

})

Remove hard code in dom-ready test case


import base from 'magnet-core/dist/base'
import get from 'lodash/get'
import isFunction from 'lodash/isFunction'

// TODO: Allow pass object as parameter

/**
 * A simple module to quickly convert nodejs module to Magnet module
 * @param  {[function, object]} module      Module to be converted
 * @param  {string} options.namespace   Namespace to occupy, same for app[namespace], config[namespace]
 * @param  {string} options.initializer Initialize function
 * @param  {[array, function]} options.params      Will pass to initializer
 * @return void
 */
export default function convert (module, { namespace, initializer, params }) {
  return class MagnetModule extends base {
    async setup () {
      const config = this.app.config[namespace]

      let moduleParams = []

      if (isFunction(params)) {
        moduleParams = [params(config)]
      } else if (Array.isArray(params)) {
        for (const param of params) {
          if (param.startsWith('config.')) {
            const configKey = param.replace('config.', '')

            moduleParams.push(get(config, configKey))
          }
        }
      } else {
        this.log.warn('Params is not recognized')
      }

      const initialize = module[initializer] || module

      this.app[namespace] = initialize(...moduleParams)
    }
  }
}

import base from './base'
import get from 'lodash/get'
import isFunction from 'lodash/isFunction'

// TODO: Allow pass object as parameter

/**
 * A simple module to quickly convert nodejs module to Magnet module
 * @param  {[function, object]} module      Module to be converted
 * @param  {string} options.namespace   Namespace to occupy, same for app[namespace], config[namespace]
 * @param  {string} options.initializer Initialize function
 * @param  {[array, function]} options.params      Will pass to initializer
 * @return void
 */
export default function convert (module, { namespace, initializer, params }) {
  return class MagnetModule extends base {
    async setup () {
      const config = this.app.config[namespace]

      let moduleParams = []

      if (isFunction(params)) {
        moduleParams = [params(config)]
      } else if (Array.isArray(params)) {
        for (const param of params) {
          if (param.startsWith('config.')) {
            const configKey = param.replace('config.', '')

            moduleParams.push(get(config, configKey))
          }
        }
      } else {
        this.log.warn('Params is not recognized')
      }

      const initialize = module[initializer] || module

      this.app[namespace] = initialize(...moduleParams)
    }
  }
}

Fix referring to wrong base


BOT.client.addListener('message' + BOT.settings.channel, function (from, message) {
    BOT.logMessage('[' + BOT.settings.channel + '] ' + from + ": " + message);

    if (message.charAt(0) == '!') {
        var parts = message.split(' ');
        var command = parts[0].substr(1, parts[0].length);
        var cmd = BOT.getCommand(command);
        if (cmd) {
            BOT.logInfo('executing command: ' + command);
            cmd(from, parts.splice(1));
        }
    }

});

BOT.client.addListener('error', function (message) {
    BOT.logInfo('error: ' + message);
});

BOT.client.addListener('registered', function (e) {
    BOT.logInfo('Now Connected to ' + e.server);
});

BOT.client.addListener('join', function (channel) {
    BOT.logInfo('Joined ' + channel);
});

BOT.client.addListener('message' + BOT.settings.channel, function (from, message) {
    BOT.logMessage('[' + BOT.settings.channel + '] ' + from + ": " + message);

    if (message.charAt(0) == '!') {
        var parts = message.split(' ');
        var command = parts[0].substr(1, parts[0].length);
        var cmd = BOT.getCommand(command);
        if (cmd) {
            BOT.logInfo('executing command: ' + command);
            if (BOT.settings.name.toLowerCase() == from.toLowerCase()) {
                setTimeout(function () {
                    cmd(from, parts.splice(1));
                }, 1200);
            } else {
                cmd(from, parts.splice(1));
            }
        }
    }

});

BOT.client.addListener('error', function (message) {
    BOT.logInfo('error: ' + message);
});

BOT.client.addListener('registered', function (e) {
    BOT.logInfo('Now Connected to ' + e.server);
});

BOT.client.addListener('join', function (channel) {
    BOT.logInfo('Joined ' + channel);
});

Check if the bot is sending the commands, and if so wait a little (twitch limitation)


// @flow
export function roundValue(value: number | string | void, placeholder: boolean | void): number | string {
    if (typeof value !== 'number') {
        return placeholder ? '—' : '';
    }
    const parsedValue = parseFloat(value.toString());
    const sizes = ['', ' K', ' M', ' G', ' T', ' P', ' E', ' Z', ' Y'];
    if (parsedValue === 0) {
        return '0';
    }
    let x = 0;
    while (Math.pow(1000, x + 1) < Math.abs(parsedValue)) {
        x++;
    }
    let prefix = (parsedValue / Math.pow(1000, x)).toFixed(2).toString();
    if (x === 0) {
        prefix = value.toFixed(2).toString();
    }
    let tailToCut = 0;
    while (prefix[prefix.length - (tailToCut + 1)] === '0') {
        tailToCut++;
    }
    if (prefix[prefix.length - (tailToCut + 1)] === '.') {
        tailToCut++;
    }
    return prefix.substring(0, prefix.length - tailToCut) + (sizes[x] || '');
}

export function getJSONContent(data: { [key: string]: any }): string {
    return 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(data, null, 2));
}

// @flow
export function roundValue(value: number | string | void, placeholder: boolean | void): number | string {
    if (typeof value !== 'number') {
        return placeholder === false ? '' : '—';
    }
    const parsedValue = parseFloat(value.toString());
    const sizes = ['', ' K', ' M', ' G', ' T', ' P', ' E', ' Z', ' Y'];
    if (parsedValue === 0) {
        return '0';
    }
    let x = 0;
    while (Math.pow(1000, x + 1) < Math.abs(parsedValue)) {
        x++;
    }
    let prefix = (parsedValue / Math.pow(1000, x)).toFixed(2).toString();
    if (x === 0) {
        prefix = value.toFixed(2).toString();
    }
    let tailToCut = 0;
    while (prefix[prefix.length - (tailToCut + 1)] === '0') {
        tailToCut++;
    }
    if (prefix[prefix.length - (tailToCut + 1)] === '.') {
        tailToCut++;
    }
    return prefix.substring(0, prefix.length - tailToCut) + (sizes[x] || '');
}

export function getJSONContent(data: { [key: string]: any }): string {
    return 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(data, null, 2));
}

Fix condition, when no placeholder for empty value


// Copyright 2020 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.module('j2cl.samples.app');

const HelloGuava = goog.require('com.google.j2cl.samples.guava.HelloGuava');

HelloGuava.getHelloWorld();

// Copyright 2020 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.module('j2cl.samples.app');

const HelloGuava = goog.require('com.google.j2cl.samples.guava.HelloGuava');

document.body.innerText = HelloGuava.getHelloWorld();

Update Guava sample to do user visible output.


$(document).ready(function() {
  var Game = function(givenBoard) {

    function getRandomIndex(maxExclusive) {
      return Math.floor(Math.random() * maxExclusive)
    }

    function randomBoard() {
      var board = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
      var index1 = getRandomIndex(16)
      var index2 = index1

      do {
        index2 = getRandomIndex(16)
      } while (index2 === index1)

      var startingNumbers = [2, 4]

      var startingNum1 = startingNumbers[getRandomIndex(2)]
      var startingNum2 = startingNumbers[getRandomIndex(2)]

      board[index1] = startingNum1
      board[index2] = startingNum2
      return board
    }

    function presetBoard(board) {
      var boardArray = board.split("")
      for (i = 0; i < boardArray.length; i++) {
       boardArray[i] = parseInt(boardArray[i])
      }
      return boardArray
    }

    if (givenBoard === undefined) {
      this.board = randomBoard()
    } else {
      this.board = presetBoard(givenBoard)
    }
  }

  Game.prototype.boardify = function() {
    return [
      this.board.slice(0,4),
      this.board.slice(4,8),
      this.board.slice(8,12),
      this.board.slice(12,16)
    ]
  }
});

Define game prototype, define board attribute, and created boardify prototype function


module.exports = {
  testURL: 'http://localhost/',
  moduleFileExtensions: ['js', 'jsx', 'json', 'styl'],
  setupFiles: ['<rootDir>/test/jestLib/setup.js'],
  moduleDirectories: ['src', 'node_modules'],
  moduleNameMapper: {
    '^redux-cozy-client$': '<rootDir>/src/lib/redux-cozy-client',
    '\\.(png|gif|jpe?g|svg)$': '<rootDir>/test/__mocks__/fileMock.js',
    // identity-obj-proxy module is installed by cozy-scripts
    styles: 'identity-obj-proxy'
  },
  transformIgnorePatterns: ['node_modules/(?!cozy-ui|cozy-harvest-lib)'],
  globals: {
    __ALLOW_HTTP__: false,
    __TARGET__: 'browser',
    __SENTRY_TOKEN__: 'token',
    cozy: {}
  }
}

module.exports = {
  testURL: 'http://localhost/',
  moduleFileExtensions: ['js', 'jsx', 'json', 'styl'],
  setupFiles: ['<rootDir>/test/jestLib/setup.js'],
  moduleDirectories: ['src', 'node_modules'],
  moduleNameMapper: {
    '^redux-cozy-client$': '<rootDir>/src/lib/redux-cozy-client',
    '^cozy-doctypes$': 'cozy-logger/dist/index.js',
    '\\.(png|gif|jpe?g|svg)$': '<rootDir>/test/__mocks__/fileMock.js',
    // identity-obj-proxy module is installed by cozy-scripts
    styles: 'identity-obj-proxy'
  },
  transformIgnorePatterns: ['node_modules/(?!cozy-ui|cozy-harvest-lib)'],
  globals: {
    __ALLOW_HTTP__: false,
    __TARGET__: 'browser',
    __SENTRY_TOKEN__: 'token',
    cozy: {}
  }
}

Fix doctypes impots in tests


'use strict';

/**
 * Module dependencies
 */
var mongoose = require('mongoose'),
  Schema = mongoose.Schema;

/**
 * Room Schema
 */
var RoomSchema = new Schema({
  code: {
    type: String,
    required: 'Please fill in the code of the room.',
    trim: true,
    unique: true
  },
  name: {
    type: String,
    trim: true
  },
  nbseats: {
    type: Number,
    required: 'Please fill in the number of seats of the room.'
  },
  pictures: {
    type: Boolean,
    default: false
  },
  map: {
    type: Schema.Types.Mixed,
    default: null
  },
  configurations: {
    type: [new Schema({
      name: {
        type: String,
        unique: true
      },
      nbseries: {
        type: Number,
        default: 1
      },
      seats: {
        type: [new Schema({
          seat: {
            type: Number,
            default: 0
          },
          serie: {
            type: Number,
            default: 0
          }
        }, {
          id: false,
          _id: false
        })],
        default: []
      }
    }, {
      id: false,
      _id: false
    })],
    default: []
  },
  created: {
    type: Date,
    default: Date.now
  },
  user: {
    type: Schema.ObjectId,
    ref: 'User'
  }
});

mongoose.model('Room', RoomSchema);

'use strict';

/**
 * Module dependencies
 */
var mongoose = require('mongoose'),
  Schema = mongoose.Schema;

/**
 * Room Schema
 */
var RoomSchema = new Schema({
  code: {
    type: String,
    required: 'Please fill in the code of the room.',
    trim: true,
    unique: true
  },
  name: {
    type: String,
    trim: true
  },
  nbseats: {
    type: Number,
    required: 'Please fill in the number of seats of the room.'
  },
  pictures: {
    type: Boolean,
    default: false
  },
  map: {
    type: Schema.Types.Mixed,
    default: null
  },
  configurations: {
    type: [new Schema({
      name: {
        type: String,
        default: 'Default configuration'
      },
      nbseries: {
        type: Number,
        default: 1
      },
      seats: {
        type: [new Schema({
          seat: {
            type: Number,
            default: 0
          },
          serie: {
            type: Number,
            default: 0
          }
        }, {
          id: false,
          _id: false
        })],
        default: []
      }
    }, {
      id: false,
      _id: false
    })],
    default: []
  },
  created: {
    type: Date,
    default: Date.now
  },
  user: {
    type: Schema.ObjectId,
    ref: 'User'
  }
});

mongoose.model('Room', RoomSchema);

Remove unique constraint on configurations subdocuments


//statement blocks

Blockly.Blocks['return_statement'] = {
  init: function() {
    this.setColour(160);
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.appendValueInput('VALUE')
      .appendField('return');
    this.setTooltip('Function returns the value of input.');
  }
};

//statement blocks

Blocklify.JavaScript.Generator['js_if_statement'] = function(block) {
  var n = 0;
  var argument = Blocklify.JavaScript.Generator.valueToCode(block, 'IF' + n,
      Blocklify.JavaScript.Generator.ORDER_NONE) || 'false';
  var branch = Blocklify.JavaScript.Generator.statementToCode(block, 'DO' + n);
  var code = 'if (' + argument + ') {\n' + branch + '}';
  for (n = 1; n <= block.elseifCount_; n++) {
    argument = Blocklify.JavaScript.Generator.valueToCode(block, 'IF' + n,
        Blocklify.JavaScript.Generator.ORDER_NONE) || 'false';
    branch = Blocklify.JavaScript.Generator.statementToCode(block, 'DO' + n);
    code += ' else if (' + argument + ') {\n' + branch + '}';
  }
  if (block.elseCount_) {
    branch = Blocklify.JavaScript.Generator.statementToCode(block, 'ELSE');
    code += ' else {\n' + branch + '}';
  }
  return code + '\n';
};

Blocklify.JavaScript.Generator['js_return_statement'] = function(block) {
  var code = 'return ' + Blocklify.JavaScript.Generator.valueToCode(block, 'VALUE',
    Blocklify.JavaScript.Generator.ORDER_NONE) + ';';
  return code + '\n';
};

Add JS if-statement and return-statement generators


(function(Modules) {
  "use strict";

  Modules.Autofocus = function() {
    this.start = function($component) {
      var forceFocus = $component.data('forceFocus');

      // if the page loads with a scroll position, we can't assume the item to focus onload
      // is still where users intend to start
      if (($(window).scrollTop() > 0) && !forceFocus) { return; }

      $('input, textarea, select', $component).eq(0).trigger('focus');

    };
  };

})(window.GOVUK.Modules);

(function(Modules) {
  "use strict";

  Modules.Autofocus = function() {
    this.start = function(component) {
      var forceFocus = $(component).data('forceFocus');

      // if the page loads with a scroll position, we can't assume the item to focus onload
      // is still where users intend to start
      if (($(window).scrollTop() > 0) && !forceFocus) { return; }

      $('input, textarea, select', component).eq(0).trigger('focus');

    };
  };

})(window.GOVUK.Modules);

Fix reference to non-existent jQuery collection


//= require jquery
//= require jquery_ujs
//= require twitter/bootstrap
//= require lodash
//= require handlebars.runtime

// To use placeholders in inputs in browsers that do not support it
// natively yet.
//= require jquery/jquery.placeholder

// Notifications (flash messages).
//= require jquery/jquery.noty

// To crop logos.
//= require jquery/jquery.Jcrop

// Used internally in the chat.
//= require jquery/jquery.autosize

// For modals.
//= require bootstrap/bootstrap-modal
//= require bootstrap/bootstrap-modalmanager

// Used in crop, modals and possibly other places. Grep for `ajaxForm`
// and `ajaxSubmit`.
//= require jquery/jquery.form

// Use to search for models (e.g. users) dynamically.
//= require select2

// Use for XMPP in the chat.
//= require strophe

//= require i18n/translations

// Datetime picker for bootstrap
//= require bootstrap-datetimepicker

// Moment.js for dates
//= require moment
//= require moment/pt-br

// TODO: remove this dependecy, this is only used in attachments now and
//       can be easily replaced by standard jquery methods.
//= require jquery/jquery.livequery

// 'base' HAS to be the first one included
//= require ./app/application/base
//= require_tree ./templates
//= require_tree ./app/application/
//= require_tree ./app/_all/

//= require jquery
//= require jquery_ujs
//= require twitter/bootstrap
//= require lodash
//= require handlebars.runtime

// To use placeholders in inputs in browsers that do not support it
// natively yet.
//= require jquery/jquery.placeholder

// Notifications (flash messages).
//= require jquery/jquery.noty

// To crop logos.
//= require jquery/jquery.Jcrop

// Used internally in the chat.
//= require jquery/jquery.autosize

// For modals.
//= require bootstrap/bootstrap-modal
//= require bootstrap/bootstrap-modalmanager

// Used in crop, modals and possibly other places. Grep for `ajaxForm`
// and `ajaxSubmit`.
//= require jquery/jquery.form

// Use to search for models (e.g. users) dynamically.
//= require select2

// Use for XMPP in the chat.
//= require strophe

//= require i18n/translations

// Moment.js for dates
//= require moment
//= require moment/pt-br

// Datetime picker for bootstrap
//= require bootstrap-datetimepicker

// TODO: remove this dependecy, this is only used in attachments now and
//       can be easily replaced by standard jquery methods.
//= require jquery/jquery.livequery

// 'base' HAS to be the first one included
//= require ./app/application/base
//= require_tree ./templates
//= require_tree ./app/application/
//= require_tree ./app/_all/

Fix moment.js not found in production


'use strict';

const validate = require('./validate'),
    _ = require('lodash'),
    assert = require('assert'),
    schema = {
        properties: {
            event: {
                type: 'string',
                required: true,
                allowEmpty: false
            },
            onEvent: {
                required: true,
                allowEmpty: false,
                conform(onEvent) {
                    return _.isFunction(onEvent);
                },
                messages: {
                    conform: 'onEvent must be a function'
                },
            },
            middleware: {
                conform(middleware) {
                    return _.isFunction(middleware) || _.isArray(middleware);
                },
                messages: {
                    conform: 'middleware must be a function or an array of functions'
                },
                required: false,
                allowEmpty: true
            }
        }
    };

function toArray(arg) {
    return !_.isArray(arg) ? [arg] : arg;
}

class Socket {

    /**
     * @description Creates a new socket instance
     * @param {String} packageName - The name of the LabShare package that created the socket definition
     * @param {Object} options
     * @param {String} options.event - The event name
     * @param {String} options.onEvent - The main event handler
     * @param {Array.<Function>|Function} options.middleware - One or more connect-style middleware functions
     */
    constructor(packageName, options = {}) {
        assert.ok(_.isString(packageName), 'Socket: "packageName" is required');
        validate({object: options, packageName, type: 'socket', schema});

        _.extend(this, options);

        this.middleware = toArray(this.middleware);
    }
}

module.exports = Socket;

'use strict';

const validate = require('./validate'),
    _ = require('lodash'),
    assert = require('assert'),
    schema = {
        properties: {
            event: {
                type: 'string',
                required: true,
                allowEmpty: false
            },
            onEvent: {
                required: true,
                allowEmpty: false,
                conform(onEvent) {
                    return _.isFunction(onEvent);
                },
                messages: {
                    conform: 'onEvent must be a function'
                },
            },
            middleware: {
                conform(middleware) {
                    return _.isFunction(middleware) || _.isArray(middleware);
                },
                messages: {
                    conform: 'middleware must be a function or an array of functions'
                },
                required: false,
                allowEmpty: true
            }
        }
    };

function toArray(arg) {
    return !_.isArray(arg) ? [arg] : arg;
}

class Socket {

    /**
     * @description Creates a new socket instance
     * @param {String} packageName - The name of the LabShare package that created the socket definition
     * @param {Object} options
     * @param {String} options.event - The event name
     * @param {String} options.onEvent - The main event handler
     * @param {Array.<Function>|Function} [options.middleware] - One or more connect-style middleware functions
     */
    constructor(packageName, options = {}) {
        assert.ok(_.isString(packageName), 'Socket: "packageName" is required');
        validate({object: options, packageName, type: 'socket', schema});

        _.extend(this, options);

        this.middleware = this.middleware || [];
        this.middleware = toArray(this.middleware);
    }
}

module.exports = Socket;

Fix middleware: an undefined value was being assigned in the Socket constructor


/*
Copyright 2015, 2016 OpenMarket Ltd

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

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

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

'use strict';

var React = require('react');

module.exports = React.createClass({
    displayName: 'VectorLoginHeader',
    statics: {
        replaces: 'LoginHeader',
    },

    render: function() {
        return (

        );
    }
});

/*
Copyright 2015, 2016 OpenMarket Ltd

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

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

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

'use strict';

var React = require('react');

module.exports = React.createClass({
    displayName: 'VectorLoginHeader',
    statics: {
        replaces: 'LoginHeader',
    },
    propTypes: {
        icon: React.PropTypes.string,
    },

    render: function() {
        return (

        );
    }
});

Allow a custom login logo to be displayed on login


import Ember from 'ember';

export default Ember.Component.extend({
    actions: {
        removeNotification: function() {
            this.notifications.removeNotification(this.get('notification'));
        }
    }
});

import Ember from 'ember';

export default Ember.Component.extend({
    classNameBindings: ['notification.type'],

    actions: {
        removeNotification: function() {
            this.notifications.removeNotification(this.get('notification'));
        }
    }
});

Add className based on type


'use strict';

/**
 * @ngdoc directive
 * @name midjaApp.directive:googlechart
 * @description
 * # googlechart
 */
angular.module('midjaApp')
  .directive('googlechart', function(dataService) {
    return {
      template: '

',
      restrict: 'E',
      link: link,
      replace: true,
      scope: {
        data: '=',
        chartobj: '='
      }
    };

    function link(scope, element, attrs) {

      var minHeight = 500;

      // export a chartobj back for triggers
      scope.chartobj = {};

      activate();

      scope.chartobj.redraw = redraw;

      function activate() {
        scope.minHeight = minHeight;
        scope.$watch('data', drawStuff);
      }

      var options = {
        legend: {
          position: "none"
        },
        bars: 'horizontal'
      };

      function drawStuff(data) {

        if (!data) {
          return;
        }
        if (!data.length) {
          return;
        }
        if (!scope.data) {
          element.html('');
          return;
        }

        var chart = new google.charts.Bar(element[0]);
        chart.draw(new google.visualization.arrayToDataTable(data), options);

        scope.chartobj.chart = chart;
      }

      function redraw() {
        scope.chartobj.chart.draw(new google.visualization.arrayToDataTable(
          scope.data), options);
      }

    }
  });

'use strict';

/**
 * @ngdoc directive
 * @name midjaApp.directive:googlechart
 * @description
 * # googlechart
 */
angular.module('midjaApp')
  .directive('googlechart', function() {
    return {
      template: '

',
      restrict: 'E',
      link: link,
      replace: true,
      scope: {
        data: '=',
        chartobj: '='
      }
    };

    function link(scope, element, attrs) {

      var minHeight = 500;

      // export a chartobj back for triggers
      scope.chartobj = {};

      activate();

      scope.chartobj.redraw = redraw;

      function activate() {
        scope.minHeight = minHeight;
        scope.$watch('data', drawStuff);
      }

      var options = {
        legend: {
          position: "none"
        },
        bars: 'horizontal'
      };

      function drawStuff(data) {

        if (!data) {
          return;
        }
        if (!data.length) {
          return;
        }
        if (!scope.data) {
          element.html('');
          return;
        }

        var chart = new google.charts.Bar(element[0]);
        chart.draw(new google.visualization.arrayToDataTable(data), options);

        scope.chartobj.chart = chart;
      }

      function redraw() {
        scope.chartobj.chart.draw(new google.visualization.arrayToDataTable(
          scope.data), options);
      }

    }
  });

Remove unneeded import of dataService


//       telegram.link
//
//       Copyright 2014 Enrico Stara 'enrico.stara@gmail.com'
//       Released under the MIT license
//       http://telegram.link

// Import dependencies
var BigInteger = require('jsbn');

// Export
exports.createMessageId = createMessageId;
exports.Logger = require('./logger');

// Create a message ID starting from local time
function createMessageId() {
    var time = new BigInteger(new Date().getTime().toString());
    time = time.divide(new BigInteger(1000..toString()));
    return time.shiftLeft(32).toString();
}

//       telegram.link
//
//       Copyright 2014 Enrico Stara 'enrico.stara@gmail.com'
//       Released under the MIT license
//       http://telegram.link

// Import dependencies
var BigInteger = require('jsbn');

// Export
exports.createMessageId = createMessageId;
exports.Logger = require('./logger');

// Create a message ID starting from local time
function createMessageId() {
    var time = new BigInteger(new Date().getTime().toString());
    time = time.divide(new BigInteger((1000).toString()));
    return time.shiftLeft(32).toString();
}

Fix the createMessageId function in utility module


import Ember from 'ember';

export default Ember.Service.extend({
    // Empty array to store favorite questions
    questions: [],

    // Push new question to questions array
    add(question) {
        this.get('questions').pushObject(question);
    }

});

import Ember from 'ember';

export default Ember.Service.extend({
    // Empty array to store favorite questions
    questions: [],

    // Push new question to questions array
    add(question) {
        this.get('questions').pushObject(question);
    },

    // Check if a question is already included in the questions array
    includes(question) {
        return this.get('questions').includes(question)
    }

});

Create includes function Create includes function that checks if a question is already in the questions array


app.translator.plural = function(count) {
  if (count >= 0 && count < 2) {
    return 'one';
    }
  else {
    return 'other';
  }
};

moment.locale('fr', {
  months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
  monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
  weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
  weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
  weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
  longDateFormat : {
    LT : 'HH:mm',
    LTS : 'HH:mm:ss',
    L : 'DD/MM/YYYY',
    LL : 'D MMMM YYYY',
    LLL : 'D MMMM YYYY HH:mm',
    LLLL : 'dddd D MMMM YYYY HH:mm'
  },
  calendar : {
    sameDay: '[Aujourd\'hui à] LT',
    nextDay: '[Demain à] LT',
    nextWeek: 'dddd [à] LT',
    lastDay: '[Hier à] LT',
    lastWeek: 'dddd [dernier à] LT',
    sameElse: 'L'
  },
  relativeTime : {
    future : 'dans %s',
    past : 'il y a %s',
    s : 'quelques secondes',
    m : 'une minute',
    mm : '%d minutes',
    h : 'une heure',
    hh : '%d heures',
    d : 'un jour',
    dd : '%d jours',
    M : 'un mois',
    MM : '%d mois',
    y : 'un an',
    yy : '%d ans'
  },
  ordinalParse: /\d{1,2}(er|)/,
  ordinal : function (number) {
    return number + (number === 1 ? 'er' : '');
  },
  week : {
    dow : 1,
    doy : 4
  }
});

Add the moment.js French localization.


var abePro = require('../src/abe-protractor.js'),
    browserMock = {
        'addMockModule': function (appName, app, data) {
            return [appName, app, data];
        }
    };

describe('ABE Protractor mockModule loading tests', function () {
    var options = {
            'mocksLocation': './tests/mocks/**',
            'stubsLocation': './',
            'log': false
        };
    // Makes a global variable for browser as called by the node module
    // to setup the Angular App (setup by Protractor)
    browser = browserMock;

    it ('adds default mock module to browser', function () {
        spyOn(browser, 'addMockModule').andCallThrough();
        abePro.setupServiceStubs(options);

        expect(browser.addMockModule).toHaveBeenCalled();

        var argsCalled = browser.addMockModule.argsForCall[0],
            appName = 'app.stubs.hello';

        expect(argsCalled[0]).toBe(appName);
        expect(typeof argsCalled[1]).toBe('function');
        expect(argsCalled[2]['stub-options']['module_name']).toBe(appName);
    });

});

var abePro = require('../src/abe-protractor.js'),
    browserMock = {
        'addMockModule': function (appName, app, data) {
            return [appName, app, data];
        }
    };

describe('ABE Protractor mockModule loading tests', function () {
    var options = {
            'mocksLocation': './tests/mocks/**',
            'stubsLocation': './',
            'log': false
        };
    // Makes a global variable for browser as called by the node module
    // to setup the Angular App (setup by Protractor)
    browser = browserMock;

    describe('Check addMockModule is called correctly', function () {
        beforeEach(function () {
            spyOn(browser, 'addMockModule').andCallThrough();
            abePro.setupServiceStubs(options);
        });

        it ('adds default mock module to browser', function () {
            expect(browser.addMockModule).toHaveBeenCalled();
        });

        it ('adds a default mock module with correct args', function () {
            var argsCalled = browser.addMockModule.argsForCall[0],
                appName = 'app.stubs.hello';

            expect(argsCalled[0]).toBe(appName);
            expect(typeof argsCalled[1]).toBe('function');
            expect(argsCalled[2]['stub-options']['module_name']).toBe(appName);
        });
    });

});

Split unit tests into two


export const dbURI = (() => {
  if (process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'test') {
    return 'mongodb://localhost/trelloService';
  } else {
    return 'mongodb://trellodb:27017/trelloService';
  }
})();

export const dbTestURI = (() => {
  if (process.env.NODE_ENV === 'docker-test') {
    return 'mongodb://trellodb:27017/trelloService';
  } else if (process.env.NODE_ENV === 'test'){
    return 'mongodb://localhost/trelloServiceTest';
  }
})();

export const usersMicroserviceUrl = (() => {
  if (process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'test') {
    return 'http://localhost:3002/users/';
  } else {
    return 'http://usersmicroservice:3002/';
  }
})();

export const port = 3001;

export const dbURI = (() => {
  if (process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'test') {
    return 'mongodb://localhost/trelloService';
  } else {
    return 'mongodb://trellodb:27017/trelloService';
  }
})();

export const dbTestURI = (() => {
  if (process.env.NODE_ENV === 'docker-test') {
    return 'mongodb://trellodb:27017/trelloService';
  } else if (process.env.NODE_ENV === 'test'){
    return 'mongodb://localhost/trelloServiceTest';
  }
})();

export const usersMicroserviceUrl = (() => {
  if (process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'test') {
    return 'http://localhost:3002/';
  } else {
    return 'http://usersmicroservice:3002/';
  }
})();

export const port = 3001;

Fix trello micro service url


ADD - Ajout de variables


import Expression from '../Expression';
import Specificity from '../Specificity';
import ArrayValue from '../dataTypes/ArrayValue';
import Sequence from '../dataTypes/Sequence';

/**
 * @extends {Expression}
 */
class CurlyArrayConstructor extends Expression {
	/**
	 * @param   {!Array<!Expression>}  members    The expressions for the values
	 */
	constructor (members) {
		super(
			new Specificity({
			[Specificity.EXTERNAL_KIND]: 1
			}),
			members,
			{
				canBeStaticallyEvaluated: members.every(member => member.canBeStaticallyEvaluated)
			});

		this._members = members;
	}

	evaluate (dynamicContext, executionParameters) {
		return this._members[0].evaluateMaybeStatically(dynamicContext, executionParameters)
			.mapAll(allValues => Sequence.singleton(new ArrayValue(allValues.map(Sequence.singleton))));
	}
}
export default CurlyArrayConstructor;

import Expression from '../Expression';
import Specificity from '../Specificity';
import ArrayValue from '../dataTypes/ArrayValue';
import Sequence from '../dataTypes/Sequence';

/**
 * @extends {Expression}
 */
class CurlyArrayConstructor extends Expression {
	/**
	 * @param   {!Array<!Expression>}  members    The expressions for the values
	 */
	constructor (members) {
		super(
			new Specificity({
			[Specificity.EXTERNAL_KIND]: 1
			}),
			members,
			{
				canBeStaticallyEvaluated: members.every(member => member.canBeStaticallyEvaluated)
			});

		this._members = members;
	}

	evaluate (dynamicContext, executionParameters) {
		if (this._members.length === 0) {
			return Sequence.singleton(new ArrayValue([]));
		}
		return this._members[0].evaluateMaybeStatically(dynamicContext, executionParameters)
			.mapAll(allValues => Sequence.singleton(new ArrayValue(allValues.map(Sequence.singleton))));
	}
}
export default CurlyArrayConstructor;

Fix arrayconstructor for empty arrays


'use strict';

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var OfferSchema = new Schema({
  title: Schema.Types.Mixed,
  description: Schema.Types.Mixed,
  price: Schema.Types.Mixed,
  createdAt: { type: Date, default: Date.now },
  active: Boolean
});

module.exports = mongoose.model('Offer', OfferSchema);

'use strict';

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var OfferSchema = new Schema({
  url: String,
  title: Schema.Types.Mixed,
  description: Schema.Types.Mixed,
  price: Schema.Types.Mixed,
  createdAt: { type: Date, default: Date.now },
  active: Boolean,
  language: String
});

OfferSchema.index({
  'url': 1
}, {
  name: 'idx_offer_url'
});

OfferSchema.index({
  'title': "text",
  'description': "text",
  'description.long': "text",
  'description.short': "text"
}, {
  name: 'idx_offer_title_description',
  default_language: "russian"
});

module.exports = mongoose.model('Offer', OfferSchema);

Add indexes to offers collection. Add language to offer schema.


import * as actionTypes from '../actionTypes/actionTypes';
import 'whatwg-fetch';

/** ACTIONS ***
Actions define the action type, and do something based on the value of 'type' (i.e. receiveUserData)
Action creators will send information to the 'store', by going through reducers, which will change
 state based on the action type. Action creators (i.e. getUserData) are executable in containers
that use Redux's 'connect' helper. 'bindActionCreators()' binds action creators to dispatch */

export const requestUserData = (data) => {
  return {
    type: actionTypes.GET_USER,
    data,
  }
}

/**
  * Receive data from fetch
**/
export const receiveUserData = (data) => {
  return {
    type: actionTypes.GET_USER_SUCCESS,
    results: data,
  }
}

/**
  * Fetch current user's information
**/
export const getUserData = () => {
  console.log('INSIDE GET USER DATA FUNCTION');
  return (dispatch) => {
    dispatch(requestUserData)

    return fetch('/get_info/', {
      method: 'get',
      mode: 'no-cors',
    }).then(res => res.json())
    .then(json => dispatch(receiveUserData(json)))
    .catch(err => dispatch({
      type: actionTypes.GET_USER_FAILURE,
      results: err,
    }))

  }
}

import * as actionTypes from '../actionTypes/actionTypes';
import 'whatwg-fetch';

/** ACTIONS ***
Actions define the action type, and do something based on the value of 'type' (i.e. receiveUserData)
Action creators will send information to the 'store', by going through reducers, which will change
 state based on the action type. Action creators (i.e. getUserData) are executable in containers
that use Redux's 'connect' helper. 'bindActionCreators()' binds action creators to dispatch */

/**
  *Default request handler
**/
export const requestUserData = (data) => {
  return {
    type: actionTypes.GET_USER,
    data,
  }
}

/**
  * Receive data from fetch
**/
export const receiveUserData = (data) => {
  return {
    type: actionTypes.GET_USER_SUCCESS,
    results: data,
  }
}

/**
  *Error handling for failed fetch
**/
export const failedFetch = (err) => {
  return {
    type: actionTypes.GET_USER_FAILURE,
    err
  }
}

/**
  * Fetch current user's information
**/
export const getUserData = () => {
  console.log('INSIDE GET USER DATA FUNCTION');
  return (dispatch) => {
    dispatch(requestUserData)

    return fetch('/get_info/', {
      method: 'get',
    }).then(res => res.json())
    .then(json => dispatch(receiveUserData(json)))
    .catch(err => dispatch(failedFetch(err)))
  }
}

Resolve bug with CORS, get user data


import { moduleFor, test } from 'ember-qunit';

moduleFor('route:login', 'Unit | Route | login', {
  // Specify the other units that are required for this test.
  // needs: ['controller:foo']
});

test('it exists', function(assert) {
  let route = this.subject();
  assert.ok(route);
});

import Ember from 'ember';
import { moduleFor, test } from 'ember-qunit';

moduleFor('route:login', 'Unit | Route | login', {
  needs: ['model:user']
});

test('it exists', function(assert) {
  let route = this.subject();
  assert.ok(route);
});

test('has a user for its model', function(assert) {
  let route = this.subject();
  Ember.run(function() {
    let user = route.model();
    assert.equal(user.get('constructor.modelName'), 'user');
  });
});

Add a login model hook test.


// -------------------------------------------------------------------------- \\
// File: SearchTextView.js                                                    \\
// Module: ControlViews                                                       \\
// Requires: TextView.js                                                      \\
// Author: Neil Jenkins                                                       \\
// License: © 2010-2015 FastMail Pty Ltd. MIT Licensed.                       \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

var ClearSearchButtonView = new NS.Class({

    Extends: NS.ButtonView,

    type: 'v-ClearSearchButton',
    positioning: 'absolute',
    label: NS.loc( 'Clear Search' ),
    shortcut: 'ctrl-/'
});

NS.ClearSearchButtonView = ClearSearchButtonView;

var SearchTextView = NS.Class({

    Extends: NS.TextView,

    type: 'v-SearchText',

    icon: null,

    draw: function ( layer, Element, el ) {
        var children =
                SearchTextView.parent.draw.call( this, layer, Element, el );
        children.push(
            this.get( 'icon' ),
            Element.when( this, 'value' ).show([
                new NS.ClearSearchButtonView({
                    target: this,
                    method: 'reset'
                })
            ]).end()
        );
        return children;
    },

    reset: function () {
        this.set( 'value', '' )
            .blur();
    }
});

NS.SearchTextView = SearchTextView;

}( O ) );

// -------------------------------------------------------------------------- \\
// File: SearchTextView.js                                                    \\
// Module: ControlViews                                                       \\
// Requires: TextView.js                                                      \\
// Author: Neil Jenkins                                                       \\
// License: © 2010-2015 FastMail Pty Ltd. MIT Licensed.                       \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

var ClearSearchButtonView = new NS.Class({

    Extends: NS.ButtonView,

    type: 'v-ClearSearchButton',
    positioning: 'absolute',
    shortcut: 'ctrl-/'
});

NS.ClearSearchButtonView = ClearSearchButtonView;

var SearchTextView = NS.Class({

    Extends: NS.TextView,

    type: 'v-SearchText',

    icon: null,

    draw: function ( layer, Element, el ) {
        var children =
                SearchTextView.parent.draw.call( this, layer, Element, el );
        children.push(
            this.get( 'icon' ),
            Element.when( this, 'value' ).show([
                new NS.ClearSearchButtonView({
                    label: NS.loc( 'Clear Search' ),
                    target: this,
                    method: 'reset'
                })
            ]).end()
        );
        return children;
    },

    reset: function () {
        this.set( 'value', '' )
            .blur();
    }
});

NS.SearchTextView = SearchTextView;

}( O ) );

Fix immediate call to NS.loc


import Connection from './Connection.js';

const API_PATH = '/users';

class RiderCardsApi {
  createUser(userData) {
    return Connection.send(API_PATH, userData, 'POST');
  }
}
export default new RiderCardsApi();

import Connection from './Connection.js';

const API_PATH = '/users';

class UsersApi {
  createUser(userData) {
    return Connection.send(API_PATH, userData, 'POST');
  }
}

export default new UsersApi();

Rename a leftover class name


export default class {
    constructor($scope, auth, toast) {
        'ngInject';

        $scope.buttons = [
            {
                name: 'View Code',
                icon: 'code',
                show: true,
                href: 'https://github.com/mjhasbach/material-qna'
            },
            {
                name: 'QnA',
                icon: 'question_answer',
                get show() {
                    return $scope.user.data && $scope.view.current !== 'qna';
                },
                onClick: function() {
                    $scope.view.current = 'qna';
                }
            },
            {
                name: 'Dashboard',
                icon: 'view_list',
                get show() {
                    return $scope.user.data && $scope.view.current !== 'dashboard';
                },
                onClick: function() {
                    $scope.view.current = 'dashboard';
                }
            },
            {
                name: 'Logout',
                icon: 'exit_to_app',
                get show() {
                    return $scope.user.data;
                },
                onClick: function() {
                    auth.logout().then(function() {
                        $scope.user.data = null;
                        $scope.view.current = 'auth';
                    }).catch(function() {
                        toast.show('Unable to log out');
                    });
                }
            }
        ];
    }
}

export default class {
    constructor($scope, auth, toast) {
        'ngInject';

        $scope.buttons = [
            {
                name: 'View Code',
                icon: 'code',
                show: true,
                href: 'https://github.com/mjhasbach/material-qna'
            },
            {
                name: 'QnA',
                icon: 'grid_on',
                get show() {
                    return $scope.user.data && $scope.view.current !== 'qna';
                },
                onClick: function() {
                    $scope.view.current = 'qna';
                }
            },
            {
                name: 'Dashboard',
                icon: 'view_list',
                get show() {
                    return $scope.user.data && $scope.view.current !== 'dashboard';
                },
                onClick: function() {
                    $scope.view.current = 'dashboard';
                }
            },
            {
                name: 'Logout',
                icon: 'exit_to_app',
                get show() {
                    return $scope.user.data;
                },
                onClick: function() {
                    auth.logout().then(function() {
                        $scope.user.data = null;
                        $scope.view.current = 'auth';
                    }).catch(function() {
                        toast.show('Unable to log out');
                    });
                }
            }
        ];
    }
}

Use grid_on icon for QnA