var util = require( "util" );
var ReadHandler = require( "express-classy" ).ReadHandler;
var User = require( "./models" ).User;

module.exports = GetDetail;

function GetDetail () {
    ReadHandler.call( this );
}

util.inherits( GetDetail, ReadHandler );

GetDetail.prototype.action = function() {

    var _this = this;

    function onDetail ( err, detail ) {

        if ( err ) {
            return _this.emit( "error", err );
        }

        return _this.emit( "done", detail );

    }

    User.detailById( this.req.user._id, onDetail );
};

var util = require( "util" );
var ReadHandler = require( "express-classy" ).ReadHandler;
var User = require( "./models" ).User;

module.exports = GetDetail;

function GetDetail () {
    ReadHandler.call( this );
}

util.inherits( GetDetail, ReadHandler );

GetDetail.prototype.action = function() {

    User.detailById( this.req.user._id, this.onDetailUpdateDelete( "done" ) );

};

Clean up user detail route


import Ember from 'ember';
import layout from '../templates/components/ilios-calendar-single-event-objective-list';

const { computed, isEmpty } = Ember;

export default Ember.Component.extend({
  layout,
  objectives: [],
  domains: computed('objectives.@each.[title,domain]', function(){
    let objectives = this.get('objectives');
    if(isEmpty(objectives)){
      return [];
    }
    let domainTitles = objectives.map(obj => {
      return obj.domain.toString();
    });
    domainTitles = Ember.A(domainTitles).uniq();
    let domains = domainTitles.map(title => {
      let domain = {
        title,
        objectives: []
      };
      domain.objectives = this.get('objectives').filter(obj => {
        return obj.domain.toString() === title;
      }).map(obj => {
        return obj.title;
      });

      return domain;
    });

    //make it an ennumerable so it can be observed
    return Ember.A(domains);
  }),
});

import Ember from 'ember';
import layout from '../templates/components/ilios-calendar-single-event-objective-list';

const { computed, isEmpty } = Ember;

export default Ember.Component.extend({
  layout,
  objectives: [],
  domains: computed('objectives.@each.[title,domain]', function(){
    let objectives = this.get('objectives');
    if(isEmpty(objectives)){
      return [];
    }
    let domainTitles = objectives.map(obj => {
      return obj.domain.toString();
    });
    domainTitles = Ember.A(domainTitles).uniq();
    let domains = domainTitles.map(title => {
      let domain = {
        title,
        objectives: []
      };
      let objectives = this.get('objectives').filter(obj => {
        return obj.domain.toString() === title;
      }).map(obj => {
        return obj.title;
      });
      domain.objectives = Ember.A(objectives).sortBy('title');

      return domain;
    });

    //make it an ennumerable so it can be observed
    return Ember.A(domains).sortBy('title');
  }),
});

Sort objectives alphabetically in single event view


// moment-fquarter.js
// version : 0.3
// author : Rob Allen
// license : MIT
// github.com/robgallen/moment-fquarter

(function () {

    function onload(moment) {
        moment.fn.fquarter = function (startMonth) {
            var adjustedDate, quarter, year, nextYear;
            startMonth = startMonth || 4; // default is April

            if (startMonth > 1) {
                adjustedDate = this.subtract("months", startMonth - 1);
				nextYear = "/" + adjustedDate.clone().add("years", 1).format("YY");
            } else {
                adjustedDate = this;
				nextYear = "";
            }

            quarter = Math.ceil((adjustedDate.month() + 1.0) / 3.0);
            year = adjustedDate.year();

            return {quarter: quarter, year: year, nextYear: year}
        };

        return moment;
    }

    if (typeof define === "function" && define.amd) {
        define("moment-fquarter", ["moment"], onload);
    } else if (typeof module !== "undefined") {
        module.exports = onload(require("moment"));
    } else if (typeof window !== "undefined" && window.moment) {
        onload(window.moment);
    }

}).apply(this);

// moment-fquarter.js
// version : 0.3
// author : Rob Allen
// license : MIT
// github.com/robgallen/moment-fquarter

(function () {

    function onload(moment) {
        moment.fn.fquarter = function (startMonth) {
            var adjustedDate, quarter, year, nextYear;
            startMonth = startMonth || 4; // default is April

            if (startMonth > 1) {
                adjustedDate = this.subtract("months", startMonth - 1);
				nextYear = "/" + adjustedDate.clone().add("years", 1).format("YYYY");
            } else {
                adjustedDate = this;
				nextYear = "";
            }

            quarter = Math.ceil((adjustedDate.month() + 1.0) / 3.0);
            year = adjustedDate.year();

            return {quarter: quarter, year: year, nextYear: nextYear}
        };

        return moment;
    }

    if (typeof define === "function" && define.amd) {
        define("moment-fquarter", ["moment"], onload);
    } else if (typeof module !== "undefined") {
        module.exports = onload(require("moment"));
    } else if (typeof window !== "undefined" && window.moment) {
        onload(window.moment);
    }

}).apply(this);

Return next fiscal year in YYYY format


const GLib = imports.gi.GLib;
const Mainloop = imports.mainloop;
const System = imports.system;

function quitMainLoopOnException(fn) {
    try {
        fn();
    } catch (e) {
        Mainloop.quit("jasmine");
        System.exit(1);
    }
}

function _setTimeoutInternal(fn, time, continueTimeout) {
    let id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, time, function () {
        quitMainLoopOnException(fn);
        return continueTimeout;
    });

    return {
        timeoutId: id
    };
}

window.setTimeout = function (fn, time) {
    return _setTimeoutInternal(fn, time, false);
};

window.setInterval = function (fn, time) {
    return _setTimeoutInternal(fn, time, true);
};

window.clearTimeout = window.clearInterval = function (id) {
    if (typeof (id) !== 'object' ||
        !Object.hasOwnProperty(id, 'timeoutId'))
        return;

    if (id.timeoutId > 0) {
        GLib.source_remove(id.timeoutId);
        id.timeoutId = 0;
    }
};

window.timer = null;

const GLib = imports.gi.GLib;
const Mainloop = imports.mainloop;
const System = imports.system;

function quitMainLoopOnException(fn) {
    try {
        fn();
    } catch (e) {
        Mainloop.quit("jasmine");
        printerr('Exception occurred outside of a test suite:', e);
        printerr(e.stack);
        System.exit(1);
    }
}

function _setTimeoutInternal(fn, time, continueTimeout) {
    let id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, time, function () {
        quitMainLoopOnException(fn);
        return continueTimeout;
    });

    return {
        timeoutId: id
    };
}

window.setTimeout = function (fn, time) {
    return _setTimeoutInternal(fn, time, false);
};

window.setInterval = function (fn, time) {
    return _setTimeoutInternal(fn, time, true);
};

window.clearTimeout = window.clearInterval = function (id) {
    if (typeof (id) !== 'object' ||
        !Object.hasOwnProperty(id, 'timeoutId'))
        return;

    if (id.timeoutId > 0) {
        GLib.source_remove(id.timeoutId);
        id.timeoutId = 0;
    }
};

window.timer = null;

Print message when quitting on exception


// Function taken from redux source
// https://github.com/reactjs/redux/blob/master/src/compose.js
function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg;
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

// Based on redux implementation of applyMiddleware to support all standard
// redux middlewares
export default function applyMiddleware(store, ...middlewares) {
  let dispatch = () => {
    throw new Error(
      'Dispatching while constructing your middleware is not allowed. '+
      'Other middleware would not be applied to this dispatch.'
    );
  };

  const middlewareAPI = {
    getState: store.getState,
    dispatch: (...args) => dispatch(...args)
  };

  middlewares = (middlewares || []).map(middleware => middleware(middlewareAPI));

  dispatch = compose(...middlewares)(store.dispatch);
  store.dispatch = dispatch;

  return store;
}

// Function taken from redux source
// https://github.com/reactjs/redux/blob/master/src/compose.js
function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg;
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

// Based on redux implementation of applyMiddleware to support all standard
// redux middlewares
export default function applyMiddleware(store, ...middlewares) {
  let dispatch = () => {
    throw new Error(
      'Dispatching while constructing your middleware is not allowed. '+
      'Other middleware would not be applied to this dispatch.'
    );
  };

  const middlewareAPI = {
    getState: store.getState.bind(store),
    dispatch: (...args) => dispatch(...args)
  };

  middlewares = (middlewares || []).map(middleware => middleware(middlewareAPI));

  dispatch = compose(...middlewares)(store.dispatch);
  store.dispatch = dispatch;

  return store;
}

Fix issue with getState not bound to store properly


/* eslint-env node */

module.exports = function(environment) {
  let ENV = {
    modulePrefix: 'dummy',
    environment,
    rootURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      },
      EXTEND_PROTOTYPES: {
        // Prevent Ember Data from overriding Date.parse.
        Date: false
      }
    },

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

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

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

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

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

  if (environment === 'production') {
    ENV.rootURL = '/ember-highcharts/';
  }

  return ENV;
};

/* eslint-env node */
'use strict';

module.exports = function(environment) {
  let ENV = {
    modulePrefix: 'dummy',
    environment,
    rootURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      },
      EXTEND_PROTOTYPES: {
        // Prevent Ember Data from overriding Date.parse.
        Date: false
      }
    },

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

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

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

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

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

  if (environment === 'production') {
    ENV.rootURL = '/ember-highcharts/';
  }

  return ENV;
};

Fix node v4 in tests by forcing strict mode


import Server from './server';
import minimist from 'minimist';

(() => {
	const args         = minimist(process.argv.slice(2));
	const port         = args.port && parseInt(args.port) || undefined;
	const isPersistent = args.hasOwnProperty('persistent');
	const clearCache   = args.hasOwnProperty('clear-cache');

	if(clearCache) {
		Server.clearCache();
	} else {
		Server.start(port, isPersistent);
	}
})();

import Server from './server';
import minimist from 'minimist';
import JsonFileManager from './database/json-file-manager';

(() => {
	const args       = minimist(process.argv.slice(2));
	const clearCache = args.hasOwnProperty('clear-cache');
	const config     = {
		port    : args.port && parseInt(args.port) || undefined,
		mode    : args.hasOwnProperty('persistent') ? 'persistent' : 'default',
		fixture : args.fixture && loadFixture(args.fixture) || undefined
	};

	if(clearCache) {
		Server.clearCache();
	} else {
		Server.start(config);
	}

	function loadFixture(path) {
		if(!path) {
			throw "You must specify path location when you use --fixture";
		}

		if(path && (path[0] === '/' || path[1] === ':')) {
			return JsonFileManager.load(path);
		} else {
			const relativePath = process.cwd();
			return JsonFileManager.load(`${relativePath}/${path}`);
		}
	}
})();

Add some changes in initialization file


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

  this.select2({
    minimumInputLength: 1,
    multiple: true,
    initSelection: function (element, callback) {
      $.get(Spree.routes.product_search, {
        ids: element.val().split(',')
      }, function (data) {
        callback(data.products);
      });
    },
    ajax: {
      url: Spree.routes.product_search,
      datatype: 'json',
      data: function (term, page) {
        return {
          q: {
            name_cont: term,
            sku_cont: term
          },
          m: 'OR',
          token: Spree.api_key
        };
      },
      results: function (data, page) {
        var products = data.products ? data.products : [];
        return {
          results: products
        };
      }
    },
    formatResult: function (product) {
      return product.name;
    },
    formatSelection: function (product) {
      return product.name;
    }
  });
};

$(document).ready(function () {
  $('.product_picker').productAutocomplete();
});

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

  this.select2({
    minimumInputLength: 1,
    multiple: true,
    initSelection: function (element, callback) {
      $.get(Spree.routes.product_search, {
        ids: element.val().split(','),
        token: Spree.api_key
      }, function (data) {
        callback(data.products);
      });
    },
    ajax: {
      url: Spree.routes.product_search,
      datatype: 'json',
      data: function (term, page) {
        return {
          q: {
            name_cont: term,
            sku_cont: term
          },
          m: 'OR',
          token: Spree.api_key
        };
      },
      results: function (data, page) {
        var products = data.products ? data.products : [];
        return {
          results: products
        };
      }
    },
    formatResult: function (product) {
      return product.name;
    },
    formatSelection: function (product) {
      return product.name;
    }
  });
};

$(document).ready(function () {
  $('.product_picker').productAutocomplete();
});

Add missing api key in product picker.


define(function(require){
    var scene = new THREE.Object3D();
    var camera = new THREE.PerspectiveCamera(75, 16 / 9, 0.1, 5000);
    var loader = new THREE.JSONLoader(true);
    var origin = new THREE.Vector3();
    var StarSystem = require("component/starfield");
    var starfield = StarSystem.create(33);

    scene.add(starfield);

    camera.position.set(0, 3, 10);
    camera.lookAt(origin);

    var ship;
    loader.load("models/spaceship-five.js", function(geometry, materials){
        materials[1].bumpMap.bumpScale = 0.0001;
        ship = new THREE.Object3D();
        ship.add(new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials)));

        var tailLight = new THREE.PointLight(0xCCCCFF, 2, 3);
        tailLight.position.set(0, 0, 5.2);
        ship.add(tailLight);

        scene.add(ship);
        console.log(ship);
    });

    var light = new THREE.PointLight(0xFFFFFF, 1, 100);
    light.position.set(10, 10, 10);
    scene.add(light);

    return {
        scene: scene,
        camera: camera,
        render: render
    }

    function render(time){
        ship.rotation.y += 0.01;
    }
});

define(function(require){
    var scene = new THREE.Object3D();
    var camera = new THREE.PerspectiveCamera(75, 16 / 9, 0.1, 5000);
    var loader = new THREE.JSONLoader(true);
    var origin = new THREE.Vector3();
    var StarSystem = require("component/starfield");
    var starfield = StarSystem.create(33, 100);

    scene.add(starfield);

    camera.position.set(0, 3, 10);
    camera.lookAt(origin);

    var ship;
    loader.load("models/spaceship-five.js", function(geometry, materials){
        materials[1].bumpMap.bumpScale = 0.0001;
        ship = new THREE.Object3D();
        ship.add(new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials)));

        var tailLight = new THREE.PointLight(0xCCCCFF, 2, 3);
        tailLight.position.set(0, 0, 5.2);
        ship.add(tailLight);

        scene.add(ship);
        console.log(ship);
    });

    var light = new THREE.PointLight(0xFFFFFF, 1, 100);
    light.position.set(10, 10, 10);
    scene.add(light);

    return {
        scene: scene,
        camera: camera,
        render: render,
        init: init
    };

    function render(time){
        ship.rotation.y += 0.01;
    }
});

Add starfield to battle scene.


/**
 * Autoincrementing Counter
 * @constructor
 */
var PrivateAutoincrementer = function() {
	var id = 0;

	/**
	 * Generate a unique ID
	 * @param {Function} cb The callback
	 */
	this.generate = function(cb) {
		var self = this;
		id++;
		if (typeof(cb) == typeof(function(){})) cb(id);
		return id;
	};

};

/**
 * UUID Singleton
 */

var Autoincrementer = (function () {
	var instance = null;
	return {
		shared: function () {
			if ( instance === null ) {
				instance = new PrivateAutoincrementer();
			}
			return instance;
		}
	};
})();

var module = module || {};
module.exports = module.exports || {};
module.exports = Autoincrementer;

/**
 * Autoincrementing Counter
 * @constructor
 */
var PrivateAutoincrementer = function() {
	var id = 0;

	/**
	 * Generate a unique ID
	 * @param {Function} cb The callback
	 */
	this.generate = function(cb) {
		var self = this;
		id++;
		if (typeof(cb) == typeof(function(){})) cb(id);
		return id;
	};

};

/**
 * UUID Singleton
 */

var Autoincrementer = (function () {
	var instance = null;
	return {
		shared: function () {
			if ( instance === null ) {
				instance = new PrivateAutoincrementer();
			}
			return instance;
		}
	};
})();

if (typeof(module) === typeof(undefined)) window.module = {};
module.exports = Autoincrementer;

Tweak for Node / Browserify / Browser unity


'use strict'
const jsonPatch = require('fast-json-patch')
const getValue = require('./../fn/getValue')
const nestingPatches = require('./../fn/nestingPatches')
const decomposePath = require('./../fn/decomposePath')
const uniq = require('uniq')
const flatten = require('./../fn/flatten')
const triggerListener = require('./../fn/triggerListener')

/**
 * patch
 *
 * Applies a patch
 */
module.exports = db => patch => {
  if (patch instanceof Array === false) {
    patch = [patch]
  }

  // Check if root exists for add operations
  patch.forEach(x => {
    let path = x.path.split('/')
    path = path.slice(0, path.length - 1).join('/')

    if (x.op === 'add' && getValue(db.static, path) === undefined) {
      let patches = nestingPatches(db.static, x.path)
      jsonPatch.apply(db.static, patches)
    }
  })

  let errors = jsonPatch.apply(db.static, patch)

  console.log(errors)

  let trigger = []

  patch.forEach(x => {
    let parts = decomposePath(x.path)
    parts.push(x.path)
    parts.forEach(y => {
      if (db.updates.triggers[y]) {
        trigger.push(db.updates.triggers[y])
      }
    })
  })

  trigger = flatten(trigger)
  trigger = uniq(trigger)

  trigger.map(x => {
    triggerListener(db, x)
  })

}

'use strict'
const jsonPatch = require('fast-json-patch')
const getValue = require('./../fn/getValue')
const nestingPatches = require('./../fn/nestingPatches')
const decomposePath = require('./../fn/decomposePath')
const uniq = require('uniq')
const flatten = require('./../fn/flatten')
const triggerListener = require('./../fn/triggerListener')

/**
 * patch
 *
 * Applies a patch
 */
module.exports = db => patch => {
  if (patch instanceof Array === false) {
    patch = [patch]
  }

  // Check if root exists for add operations
  patch.forEach(x => {
    let path = x.path.split('/')
    path = path.slice(0, path.length - 1).join('/')

    if (x.op === 'add' && getValue(db.static, path) === undefined) {
      let patches = nestingPatches(db.static, x.path)
      jsonPatch.apply(db.static, patches)
    }
  })

  let errors = jsonPatch.apply(db.static, patch)

  let trigger = []

  patch.forEach(x => {
    let parts = decomposePath(x.path)
    parts.push(x.path)
    parts.forEach(y => {
      if (db.updates.triggers[y]) {
        trigger.push(db.updates.triggers[y])
      }
    })
  })

  trigger = flatten(trigger)
  trigger = uniq(trigger)

  trigger.map(x => {
    triggerListener(db, x)
  })

}

Remove console log with errors


const UserDB = require('../db/userdb').Users
exports.ui = (req, res, next) => {
  if (req.session) {
    let name = req.session.user.userName
    console.log('</routes/chat.js , ui> userName -> ', name)
    req.userName = res.locals.userName = name
    getAllUsers(res, name)
  } else {
    res.redirect('/')
  }
}

getAllUsers = (res, name) => {
  UserDB.all((err, users) => {
    //console.log('<chat.js, getAllUsers > users -> ', users)
    if (err) throw new Error(err)
    if (users.length > 0) {
      res.render('chat_ui', {
        userName: name,
        users: users
      })
    } else {
      res.render('chat_ui', {
        userName: name,
        users: []
      })
    }
  })
}

const UserDB = require('../db/userdb').Users
exports.ui = (req, res, next) => {
  if (req.session && req.session.user) {
    let name = req.session.user.userName
    console.log('</routes/chat.js , ui> userName -> ', name)
    req.userName = res.locals.userName = name
    getAllUsers(res, name)
  } else {
    res.redirect('/')
  }
}

getAllUsers = (res, name) => {
  UserDB.all((err, users) => {
    //console.log('<chat.js, getAllUsers > users -> ', users)
    if (err) throw new Error(err)
    if (users.length > 0) {
      res.render('chat_ui', {
        userName: name,
        users: users
      })
    } else {
      res.render('chat_ui', {
        userName: name,
        users: []
      })
    }
  })
}

Add check for user existance inside session


// Polyfills
import 'babel-polyfill';

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

import { Router } from 'react-router';

import { createHashHistory } from 'history';
import routes from './routes';

import 'styles/main.less';

const history = createHashHistory();

const content = document.getElementById('content');

ReactDOM.render(<Router history={history}>{routes}</Router>, content);

// Polyfills
import 'babel-polyfill';

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

import { Router, hashHistory } from 'react-router';

import routes from './routes';

import 'styles/main.less';

const content = document.getElementById('content');

ReactDOM.render(<Router history={hashHistory}>{routes}</Router>, content);

Use history directly from `react-router`


/*
 *  Copyright (c) Codiad & Kent Safranski (codiad.com), distributed
 *  as-is and without warranty under the MIT License. See
 *  [root]/license.txt for more. This information must remain intact.
 */

(function(global, $){
    global.codiad.terminal = {

        termWidth: $(window)
            .outerWidth() - 500,

        open: function() {
            codiad.modal.load(this.termWidth, 'components/terminal/dialog.php');
        }

    };
})(this, jQuery);

/*
 *  Copyright (c) Codiad & Kent Safranski (codiad.com), distributed
 *  as-is and without warranty under the MIT License. See
 *  [root]/license.txt for more. This information must remain intact.
 */

(function(global, $){
    global.codiad.terminal = {

        termWidth: $(window)
            .outerWidth() - 500,

        open: function() {
            codiad.modal.load(this.termWidth, 'components/terminal/dialog.php');
            codiad.modal.hideOverlay();
        }

    };
})(this, jQuery);

Remove overlay so files/editor still accessible when terminal is open


const winston = require('winston');
const winstonError = require('winston-error');

const levels = {
  levels: {
    error: 0,
    warn: 1,
    info: 2,
    verbose: 3,
    debug: 4,
    silly: 5,
    database: 6,
  },
};

const consoleLogger = new (winston.Logger)({ levels: levels.levels });

if (process.env.NODE_ENV !== 'test') {
  consoleLogger.add(winston.transports.Console, {
    level: process.env.SDF_LOGLEVEL || 'database',
    exitOnError: false,
    handleExceptions: true,
    humanReadableUnhandledException: true,
    timestamp: true,
    colorize: process.env.SDF_COLORIZE_LOGS.toLowerCase() === 'true',
  });
}

if (process.env.SDF_CATCH_UNHANDLED_REJECTIONS === 'true' && process.env.NODE_ENV !== 'test') {
  consoleLogger.info('Catching unhandled rejections.');
  process.on('unhandledRejection', msg => consoleLogger.warn('Caught unhandled rejection:', msg));
}

winstonError(consoleLogger);

module.exports = consoleLogger;

const winston = require('winston');
const winstonError = require('winston-error');

const levels = {
  levels: {
    error: 0,
    warn: 1,
    info: 2,
    verbose: 3,
    debug: 4,
    silly: 5,
    database: 6,
  },
};

const consoleLogger = new (winston.Logger)({ levels: levels.levels });

if (process.env.NODE_ENV !== 'test') {
  consoleLogger.add(winston.transports.Console, {
    level: process.env.SDF_LOGLEVEL || 'database',
    exitOnError: false,
    handleExceptions: true,
    humanReadableUnhandledException: true,
    timestamp: true,
    colorize: process.env.SDF_COLORIZE_LOGS === 'true',
  });
}

if (process.env.SDF_CATCH_UNHANDLED_REJECTIONS === 'true' && process.env.NODE_ENV !== 'test') {
  consoleLogger.info('Catching unhandled rejections.');
  process.on('unhandledRejection', msg => consoleLogger.warn('Caught unhandled rejection:', msg));
}

winstonError(consoleLogger);

module.exports = consoleLogger;

Remove toLowerCase so that the env var doesn't need to be set


#!/usr/bin/env node

var cdbExport = require('cartodb-export');
var fs = require('fs');
var program = require('commander');

var cartodb2leaflet = require('../index');

program
    .version('0.0.1')
    .usage('[options] url')
    .option('-d, --dir [directory]', 'Specify the output directory [.]', '.')
    .parse(process.argv);

if (!program.args.length) {
    console.log('Please enter a url');
    program.outputHelp();
    process.exit(1);
}

fs.readFile('/dev/stdin', 'utf8', function (error, contents) {
    console.log(JSON.stringify(cartocss2leaflet(contents)));
});

var url = program.args[0];
console.log('Saving visualization in ' + program.dir);
cdbExport.exportVis(url, program.dir, function (err) {
    if (err) {
        console.error('Error while exporting visualization:', err);
        return;
    }

    console.log('Converting visualization to Leaflet');
    cartodb2leaflet.toLeaflet(program.dir, program.dir, function (err) {
        if (err) {
            console.error('Error while converting visualization to Leaflet:', err);
            return;
        }
        console.log('Done converting visualization to Leaflet');
    });
});

#!/usr/bin/env node

var cdbExport = require('cartodb-export');
var fs = require('fs');
var program = require('commander');

var cartodb2leaflet = require('../index');

program
    .version('0.0.1')
    .usage('[options] url')
    .option('-d, --dir [directory]', 'Specify the output directory [.]', '.')
    .parse(process.argv);

if (!program.args.length) {
    console.log('Please enter a url');
    program.outputHelp();
    process.exit(1);
}

fs.readFile('/dev/stdin', 'utf8', function (error, contents) {
    console.log(JSON.stringify(cartocss2leaflet(contents)));
});

var url = program.args[0];
console.log('Saving visualization in ' + program.dir);
cdbExport.exportVis(url, program.dir, function (err) {
    if (err) {
        console.error('Error while exporting visualization:', err);
        return;
    }

    console.log('Converting visualization to Leaflet');
    cartodb2leaflet.toLeaflet(program.dir, program.dir, function (err) {
        if (err) {
            console.error('Error while converting visualization to Leaflet:', err);
            return;
        }
        console.log('Done converting visualization to Leaflet');
        process.exit(0);
    });
});

Use process.exit once final callback is called


var NO_DATA = 'no data';

module.exports = {

  prepareModuleForRender: function (dashboardConfig, module) {
    var data = {
      departmentCode: dashboardConfig.department.abbr.toLowerCase(),
      dashboardSlug: dashboardConfig.slug,
      dashboardTitle: dashboardConfig.title,
      moduleType: module['module-type'],
      title: module.title,
      latest: module.data[0] || null,
      secondLatest: module.data[1] || null
    };

    data.displaySlide = this.displaySlide(data);
    if (data.displaySlide) {
      data = this.checkForMissingData(data);
    }
    return data;
  },

  displaySlide: function (data) {
    var returnVal = true;

    if ((!data.latest && !data.secondLatest) ||
      ((data.latest.formatted_value === NO_DATA) &&
      (data.secondLatest.formatted_value === NO_DATA))) {
      returnVal = false;
    }
    return returnVal;
  },

  checkForMissingData: function (data) {
    if (data.latest.formatted_value !== NO_DATA) {
      data.latestAvailable = true;
    }
    if (data.secondLatest && (data.secondLatest.formatted_value !== NO_DATA)) {
      data.secondLatestAvailable = true;
    }
    data.showChange = data.latestAvailable && data.secondLatestAvailable;
    data.showSecondLatest = !data.latestAvailable && data.secondLatestAvailable;

    return data;
  }

};

Split data processing / checks to separate module


var env = require('./environment.js');

// A small suite to make sure the cucumber framework works.
exports.config = {
  seleniumAddress: env.seleniumAddress,

  framework: 'custom',
  frameworkPath: '../index.js',

  // Spec patterns are relative to this directory.
  specs: [
    'cucumber/*.feature'
  ],

  multiCapabilities: [
    {
      browserName: 'chrome',
      version: 'ANY',
      cucumberOpts: {
        tags: '@dev',
        format: 'pretty'
      }
    }
  ],

  baseUrl: env.baseUrl,

  cucumberOpts: {
    require: 'cucumber/**/stepDefinitions.js',
    tags: '@failing',
    format: 'progress',
    profile: false,
    'no-source': true
  }
};

var env = require('./environment.js');

// A small suite to make sure the cucumber framework works.
exports.config = {
  seleniumAddress: env.seleniumAddress,

  framework: 'custom',
  frameworkPath: '../index.js',

  // Spec patterns are relative to this directory.
  specs: [
    'cucumber/*.feature'
  ],

  multiCapabilities: [
    {
      browserName: (process.env.TEST_BROWSER_NAME || 'chrome'),
      version: (process.env.TEST_BROWSER_VERSION || 'ANY'),
      cucumberOpts: {
        tags: '@dev',
        format: 'pretty'
      }
    }
  ],

  baseUrl: env.baseUrl,

  cucumberOpts: {
    require: 'cucumber/**/stepDefinitions.js',
    tags: '@failing',
    format: 'progress',
    profile: false,
    'no-source': true
  }
};

Fix browser conflicts for test suite


var hoverZoomPlugins = hoverZoomPlugins || [];
hoverZoomPlugins.push({
    name:'Art UK',
    prepareImgLinks:function (callback) {
        var res = [];
        $('div.credit').each(function () {
            $(this).css("pointer-events", "none");
        });
        $('body').on('mouseenter', 'img[src*="static.artuk.org"]', function() {
            var img = $(this);
            var url = img.attr('src').replace(/w\d+(h\d+)?/, 'w800h800');
            img.data().hoverZoomSrc = [url];
            img.addClass('hoverZoomLink');
            hoverZoom.displayPicFromElement(img);
        });
        callback($(res));
    }
});

var hoverZoomPlugins = hoverZoomPlugins || [];
hoverZoomPlugins.push({
    name:'Art UK',
    prepareImgLinks:function (callback) {
        var res = [];
        $('div.credit').each(function () {
            $(this).css("pointer-events", "none");
        });
         hoverZoom.urlReplace(res,
            'img[src*="static.artuk.org"]',
            /w\d+(h\d+)?/,
            'w800h800'
        );
        callback($(res));
    }
});

Use urlReplace as opposed to event listener


/**
 * Ember.js plugin
 *
 * Patches event handler callbacks and ajax callbacks.
 */
;(function(window, Raven, Ember) {
'use strict';

// quit if Ember isn't on the page
if (!Ember) {
    return;
}

var _oldOnError = Ember.onerror;
Ember.onerror = function EmberOnError(error) {
    Raven.captureException(error);
    if (typeof _oldOnError === 'function') {
        _oldOnError.call(this, error);
    }
};
Ember.RSVP.on('error', function (err) {
  Raven.captureException(err);
});

}(window, window.Raven, window.Ember));

/**
 * Ember.js plugin
 *
 * Patches event handler callbacks and ajax callbacks.
 */
;(function(window, Raven, Ember) {
'use strict';

// quit if Ember isn't on the page
if (!Ember) {
    return;
}

var _oldOnError = Ember.onerror;
Ember.onerror = function EmberOnError(error) {
    Raven.captureException(error);
    if (typeof _oldOnError === 'function') {
        _oldOnError.call(this, error);
    }
};
Ember.RSVP.on('error', function (reason) {
    if (reason instanceof Error) {
        Raven.captureException(reason);
    } else {
        try {
            throw new Error('Unhandled Promise error detected');
        } catch (err) {
            Raven.captureException(err, {extra: {reason: reason}});
        }
    }
});

}(window, window.Raven, window.Ember));

Send unhandled RSVP errors that don't provide an Error.


import React from 'react';
import RIEStatefulBase from './RIEStatefulBase';

export default class RIENumber extends RIEStatefulBase {

    constructor(props) {
        super(props);
    }

    static propTypes = {
        format: React.PropTypes.func
    };

    validate = (value) => {
        return !isNaN(value) && isFinite(value) && value.length > 0;
    };

    renderNormalComponent = () => {
        return <span
            tabIndex="0"
            className={this.makeClassString()}
            onFocus={this.startEditing}
            onClick={this.startEditing}>{this.props.format ? this.props.format(this.state.newValue || this.props.value) : (this.state.newValue || this.props.value)}</span>;
    };

    renderEditingComponent = () => {
        return <input disabled={(this.props.shouldBlockWhileLoading && this.state.loading)}
                      className={this.makeClassString()}
                      defaultValue={this.props.value}
                      onInput={this.textChanged}
                      onBlur={this.finishEditing}
                      ref="input"
                      onKeyDown={this.keyDown} />;
    };
}

import React from 'react';
import RIEStatefulBase from './RIEStatefulBase';

export default class RIENumber extends RIEStatefulBase {

    constructor(props) {
        super(props);
    }

    static propTypes = {
        format: React.PropTypes.func
    };

    validate = (value) => {
        return !isNaN(value) && isFinite(value) && value.length > 0;
    };

    renderNormalComponent = () => {
        return <span
            tabIndex="0"
            className={this.makeClassString()}
            onFocus={this.startEditing}
            onClick={this.startEditing}>{this.props.format ? this.props.format(this.state.newValue || this.props.value) : (this.state.newValue || this.props.value)}</span>;
    };

    renderEditingComponent = () => {
        return <input disabled={(this.props.shouldBlockWhileLoading && this.state.loading)}
                      type="number"
                      className={this.makeClassString()}
                      defaultValue={this.props.value}
                      onInput={this.textChanged}
                      onBlur={this.finishEditing}
                      ref="input"
                      onKeyDown={this.keyDown} />;
    };
}

Make input type = "number"


import React from 'react';
import { connect } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
import Navbar from './Navbar';
import PageFooter from './PageFooter';
import Favorites from './Favorites';
import Profile from './Profile';
import getUserFavorites from '../actions/favorites';
import getUserProfile from '../actions/profile';

const User = props => (
  <div>
        <Navbar />
        <Switch>
          <Route exact path={`${props.match.url}/:userId/favorites`} render= { () => (
            <Favorites { ...props } />
          )}/>
          <Route exact path={`${props.match.url}/:userId/profile`} render= { () => (
            <Profile { ...props } />
          )}/>
        </Switch>
        <PageFooter />
      </div>
);

const mapStateToProps = state => ({
  ...state.login,
  ...state.favorites,
  ...state.profile,
});
const mapDispatchToProps = dispatch => ({
  getUserFavorites: (userId) => { dispatch(getUserFavorites(userId)); },
  getUserProfile: (userId) => { dispatch(getUserProfile(userId)); },
});
export default connect(mapStateToProps, mapDispatchToProps)(User);

import React from 'react';
import { connect } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
import Navbar from './Navbar';
import PageFooter from './PageFooter';
import Favorites from './Favorites';
import Profile from './Profile';
import getUserFavorites from '../actions/favorites';
import { getUserProfile, returnBook } from '../actions/profile';

const User = props => (
  <div>
        <Navbar />
        <Switch>
          <Route exact path={`${props.match.url}/:userId/favorites`} render= { () => (
            <Favorites { ...props } />
          )}/>
          <Route exact path={`${props.match.url}/:userId/profile`} render= { () => (
            <Profile { ...props } />
          )}/>
        </Switch>
        <PageFooter />
      </div>
);

const mapStateToProps = state => ({
  ...state.login,
  ...state.favorites,
  ...state.profile,
});
const mapDispatchToProps = dispatch => ({
  getUserFavorites: (userId) => { dispatch(getUserFavorites(userId)); },
  getUserProfile: (userId) => { dispatch(getUserProfile(userId)); },
  returnBook: (userId, bookId) => {
    dispatch(returnBook(userId, bookId));
  },
});
export default connect(mapStateToProps, mapDispatchToProps)(User);

Connect returnBook method to user


/* @flow */
import firebase from 'firebase/app'

// Initiates Firebase auth and listen to auth state changes
const initialize = (config: Object, store: Object) => {
  firebase.initializeApp(config)

  firebase.auth().onAuthStateChanged((user: ?Object) => {
    if (user) {
      store.dispatch({ type: 'USER_LOGGED_IN' })
    } else {
      store.dispatch({ type: 'LOGOUT' })
    }
  })
}

export default initialize

/* @flow */
import firebase from 'firebase/app'

// Initiates Firebase auth and listen to auth state changes
const initialize = (config: Object, store: Object) => {
  firebase.initializeApp(config)

  firebase.auth().onAuthStateChanged((user: ?Object) => {
    if (user) {
      store.dispatch({ type: 'USER_LOGGED_IN' })
    } else {
      store.dispatch({ type: 'LOGOUT' })
      store.dispatch({ type: 'APP_FINISHED_LOADING' })
    }
  })
}

export default initialize

Fix issue where app did not load for logged-out user.


describeMixin('search/types/withSearch', function() {

    var mockConcepts;

    beforeEach(function() {
        setupComponent();

        mockConcepts = function(concepts) {
            var self = this,
                c = this.component,
                d = $.Deferred()

            c.on('serviceRequest', function(event, data) {
                if (data.service === 'ontology' && data.method === 'concepts') {
                    c.trigger('serviceRequestCompleted', {
                        requestId: data.requestId,
                        success: true,
                        result: concepts
                    })
                    _.defer(d.resolve)
                }
            })
            return d
        }.bind(this);
    })

    it('should show entities row with loading on search', function(done) {
        var c = this.component;
        c.trigger('searchRequestBegan');

        var entitiesRow = c.$node.find('li')
        entitiesRow.should.not.be.empty

        mockConcepts([])
            .done(function() {
                expect(c.$node.find('.search-concepts li .badge.loading').length).to.equal(0)
                done()
            })
        c.trigger('searchRequestCompleted', { success: true, results: {} });
    })

    it.only('should show concepts for results', function(done) {
        var c = this.component;
        c.trigger('searchRequestBegan');

        mockConcepts({
            byId: {
                thing: { displayName: 'thing', id: 'thingid', pluralDisplayName: 'things' },
                raw: { displayName: 'raw', id: 'rawid', pluralDisplayName: 'raws', parentConcept: 'thing' },
                doc: { displayName: 'doc', id: 'docid', pluralDisplayName: 'docs', parentConcept: 'raw' },
                pdf: { displayName: 'pdf', id: 'pdfid', pluralDisplayName: 'pdfs', parentConcept: 'doc' }
            }
        })
            .done(function() {
                expect(c.$node.find('.search-concepts li').length).to.equal(4)

                var item = c.$node.find('.search-concepts li.nav-header').next('li')

                item.find('.title').text().should.equal('raw')
                item = item.next().find('.title').text().should.equal('doc')
                item = item.next().find('.title').text().should.equal('pdf')

                done()
            })

        c.trigger('searchRequestCompleted', {
            success: true,
            results: {
                verticesCount: {
                    pdf: 1,
                    doc: 1
                }
            }
        });
    })

    it('should clear concepts on clearSearch events', function() {
        var c = this.component

        c.trigger('searchRequestBegan');
        c.$node.find('li .badge.loading').toArray().should.not.be.empty

        c.trigger('clearSearch')
        c.select('conceptsSelector').html().should.be.empty
    })

    it('should clear filters on clearSearch events')
})

describeMixin('search/types/withSearch', function() {

    beforeEach(function() {
        setupComponent();
    })

    it('should show loading spinner on search')

    it('should hide loading spinner on search complete')

    it('should clear filters on clearSearch events')
})

Remove tests that test concept hierarchy


const test = require('tape')
const BorrowState = require('borrow-state')

test(`read-only-safety`, (t) => {
  t.plan(4)
  let myState = new BorrowState()
  myState.block().then((state) => {
    t.ok(true)
    state.foo = Math.PI
    state.unblock()
  })
  myState.block('r').then((state) => {
    t.ok(true)
    state.foo = 5
    state.unblock()
  })
  myState.block().then((state) => {
    t.ok(true)
    t.equal(state.foo, Math.PI)
    state.unblock()
  })
})

const test = require('tape')
const BorrowState = require('borrow-state')

test(`read-only-safety`, (t) => {
  t.plan(2)
  let hasHappened = []
  let myState = new BorrowState()
  myState.block().then((state) => {
    hasHappened.push(1)
    state.foo = Math.PI
    state.unblock()
  })
  myState.block('r').then((state) => {
    hasHappened.push(2)
    state.foo = 5
    state.unblock()
  })
  // The following is not set to read-only so that it can occur *after* the previous
  myState.block().then((state) => {
    hasHappened.push(3)
    t.equal(state.foo, Math.PI, 'The state should still be set to pi')
    t.equal(hasHappened, [1, 2, 3], 'The operations happened in the right order')
    state.unblock()
  })
})

Make the test actually work


var colors = require('colors');

var CargoLog = function () {
    var scope = this;
    this.verbose = false;
    return function (cargo) {
        scope.cargo = cargo;
        return scope.log;
    };
};

/**
 * Log to the console
 * @return null
 */
CargoLog.prototype.log = function () {
    var args = Array.prototype.slice.call(arguments);
    var color = 'blue';

    if (['log', 'error', 'throw'].indexOf(args[0]) != -1) {
        var type = args.shift();
        switch (type) {
            case 'error':
                color = 'yellow';
                break;
            case 'throw':
                color = 'red';
                break;
        }
    }
    args.unshift(colors[color]('[cargo]'));
    // if (this.verbose)
    console.log.apply(this, args);
};

/**
 * Log an error to the console
 * @return null
 */
CargoLog.prototype.error = function () {
    var text = Array.prototype.slice.call(arguments);
    text.unshift('error');
    this.log.apply(this, text);
};

/**
 * Throw an error with the given message and halt execution
 * @return null
 */
CargoLog.prototype.throw = function () {
    var text = Array.prototype.slice.call(arguments);
    text.unshift('throw');
    this.log.apply(this, text);
    process.exit(1);
};

module.exports = new CargoLog;

var colors = require('colors');

var CargoLog = function () {
    var scope = this;
    this.verbose = false;
    return function (cargo) {
        scope.cargo = cargo;
        return scope.log;
    };
};

/**
 * Log to the console
 * @return null
 */
CargoLog.prototype.log = function () {
    var args = Array.prototype.slice.call(arguments);
    var color = 'blue';

    if (['log', 'error', 'throw'].indexOf(args[0]) != -1) {
        var type = args.shift();
        switch (type) {
            case 'error':
                color = 'yellow';
                break;
            case 'throw':
                color = 'red';
                break;
        }
    }
    args.unshift(colors[color]('[cargo]'));
    // if (this.verbose)
    console.log.apply(this, args);
    if (type && type == 'throw') {
        process.exit(1);
    }
};

/**
 * Log an error to the console
 * @return null
 */
CargoLog.prototype.error = function () {
    var text = Array.prototype.slice.call(arguments);
    text.unshift('error');
    this.log.apply(this, text);
};

/**
 * Throw an error with the given message and halt execution
 * @return null
 */
CargoLog.prototype.throw = function () {
    var text = Array.prototype.slice.call(arguments);
    text.unshift('throw');
    this.log.apply(this, text);
    process.exit(1);
};

module.exports = new CargoLog;

Quit with error code when log('throw') is called


import Vue from 'vue';
import VueResource from 'vue-resource';
Vue.use(VueResource);
Vue.http.interceptors.push((request, next) => {
    request.credentials = true;
    next();
});

module.exports = function (option) {
    // service, action, params = {}, url = 'http://localhost:8080'
    // const SYSTEM_ENV = 'online';
    let SERVER = option.url || 'http://localhost:8080';
    let service = option.service || '';
    let action = option.action || '';
    let params = option.params || {};
    let http = new Vue();
    // let that = this;
    // Include api function.
    function api (service, action, params) {
        let httpObj = {};
        switch (service) {
        default :
            httpObj = {
                login : { method : 'GET', url : SERVER + '/login/test/{id}' },
                logout : { method : 'POST', url : SERVER + '/login/test/' }
            };
            return httpObj;
        }
    };
    let obj = {
        api : http.$resource(SERVER, {}, api(service, action, params))[action](params).then((res) => {
            return res.body;
        }, (error) => {
            return error;
        })
    };
    return obj;
};

import Vue from 'vue';
import VueResource from 'vue-resource';
Vue.use(VueResource);
Vue.http.interceptors.push((request, next) => {
    request.credentials = true;
    // console.log(next);
    next(function (response) {
        console.log(response.status);
        // if (response.ok === false) {
        //     return false;
        // }
    }, (response) => {
        console.log('error');
    });
});

module.exports = function (option) {
    // service, action, params = {}, url = 'http://localhost:8080'
    // const SYSTEM_ENV = 'online';
    let SERVER = option.url || 'http://localhost:8080';
    let service = option.service || '';
    let action = option.action || '';
    let params = option.params || {};
    let http = new Vue();
    // let that = this;
    // Include api function.
    function api (service, action, params) {
        let httpObj = {};
        switch (service) {
        default :
            httpObj = {
                login : { method : 'GET', url : SERVER + '/login/test/{id}' },
                logout : { method : 'POST', url : SERVER + '/login/test/' }
            };
            return httpObj;
        }
    };
    let obj = {
        api : http.$resource(SERVER, {}, api(service, action, params))[action](params).then((res) => {
            return res.body;
        }, (error) => {
            return error;
        })
    };
    return obj;
};

Set Vue-interceptors console.log to HTTP Status code.


export default (petition = {}) => {
  return petition.city && petition.city.data
    ? petition.city.data.name
    : '';
};

export default (petition = {}) => {
  const city = petition.city && petition.city.data;
  return city ? `${city.name} - ${city.zips[0]}` : '';
};

Add zip to city label


import path from 'path';
import objectPath from 'object-path';
import Q from 'q';
import hashFile from './hashFile';

export default function verifyFiles(cwd, script, root) {
  const deferred = Q.defer();
  const top = root ? objectPath.get(script, root) : script;
  const hashes = Object.keys(top.files);

  const next = () => {
    if (hashes.length === 0) {
      deferred.resolve();
      return;
    }

    const hash = hashes.shift();
    const file = top.files[hash];

    hashFile(cwd, path.resolve(cwd, file), top.algorithm)
      .then(h => {
        if (hash !== h) {
          deferred.reject(new Error('Mismatch: ' + file));
        }
        next();
      })
      .catch(deferred.reject);
  };

  next();

  return deferred.promise;
}

import path from 'path';
import objectPath from 'object-path';
import Q from 'q';
import hashFile from './hashFile';

export default function verifyFiles(cwd, script, root) {
  const deferred = Q.defer();
  const top = root ? objectPath.get(script, root) : script;
  const hashes = Object.keys(top.files);
  const errors = [];

  const next = () => {
    if (hashes.length === 0) {
      if (errors.length > 0) {
        deferred.reject(new Error(errors.map(err => err.message).join('\n')));
      } else {
        deferred.resolve();
      }
      return;
    }

    const hash = hashes.shift();
    const file = top.files[hash];

    hashFile(cwd, path.resolve(cwd, file), top.algorithm)
      .then(h => {
        if (hash !== h) {
          errors.push(new Error('Mismatch: ' + file));
        }
      })
      .catch(err => errors.push(err))
      .finally(next);
  };

  next();

  return deferred.promise;
}

Verify will now output all mismatches


#!/usr/bin/env node

import 'source-map-support/register'

import yargs from 'yargs'
import rc from 'rc'
import {basename as pathBasename} from 'path'

const config = rc('lenticular')

yargs
  .commandDir('cmds')
  .demandCommand()
  .options({
    'product-name': {
      desc: 'Name of your product. Short and unique',
      default: config.productName || pathBasename(process.cwd()),
      group: 'Global Options:',
    },
    'build-region': {
      desc: `Region for product's build pipeline`,
      default: config.buildRegion || 'us-west-2',
      group: 'Global Options:',
    },
  })
  .group('help', 'Global Options:')
  .config(config)
  .help()
  .argv

process.on('unhandledRejection', err => {
  throw err
})

#!/usr/bin/env node

import 'source-map-support/register'

import yargs from 'yargs'
import rc from 'rc'
import {basename as pathBasename} from 'path'

const config = rc('lenticular')

yargs
  .commandDir('cmds')
  .demandCommand()
  .options({
    'product-name': {
      desc: 'Name of your product. Short and unique',
      default: config.productName || pathBasename(process.cwd()),
      group: 'Global Options:',
    },
    'build-region': {
      desc: `Region for product's build pipeline`,
      default: config.buildRegion || 'us-west-2',
      group: 'Global Options:',
    },
    'product-dir': {
      desc: `Path to the product (project) directory`,
      default: process.cwd(),
      group: 'Global Options:',
    }
  })
  .group('help', 'Global Options:')
  .config(config)
  .help()
  .argv

process.on('unhandledRejection', err => {
  throw err
})

Fix missing productDir config option


var request = require('request')
  ;

module.exports.query = function(req,res) {

  var badCharsAddr = /[^A-Za-z0-9\.\-\#\s]/g;
  var addr = req.param('address') || '';
  addr = addr.replace(badCharsAddr,'');
  var city = 'Chicago';
  var state = 'IL';
  var zip = req.param('zip') || '';
  zip = zip.replace(/[^0-9\-\s]/g,'');
  var location = [addr,city,state,zip].join(',');

  var url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + location;
  request.get(url, function(err, resp, body){
    if (err) {
      console.error(err);
      return res.send(500);
    }

    if (resp.statusCode !== 200) {
      console.error("GOOGLE API FAILURE: " + resp.statusCode);
      return res.send(resp.statusCode);
    }

    var body = JSON.parse(body);
    res.json(body.results);
  });
}

var request = require('request')
  ;

module.exports.query = function(req,res) {

  var badCharsAddr = /[^A-Za-z0-9\.\-\#\s]/g;
  var addr = req.param('address') || '';
  addr = addr.replace(badCharsAddr,'');
  var city = 'Chicago';
  var state = 'IL';
  var zip = req.param('zip') || '';
  zip = zip.replace(/[^0-9\-\s]/g,'');
  var location = [addr,city,state,zip].join(',');

  var url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + location;
  request.get(url, function(err, resp, body){
    if (err) {
      console.error(err);
      return res.sendStatus(500);
    }

    if (resp.statusCode !== 200) {
      console.error("GOOGLE API FAILURE: " + resp.statusCode);
      return res.send(resp.statusCode);
    }

    var body = JSON.parse(body);
    res.json(body.results);
  });
}

Fix deprecated method: res.send() to res.sendStatus()


/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 */

'use strict';

import Platform from '../../Utilities/Platform';
import typeof AnimatedFlatList from './components/AnimatedFlatList';
import typeof AnimatedImage from './components/AnimatedImage';
import typeof AnimatedScrollView from './components/AnimatedScrollView';
import typeof AnimatedSectionList from './components/AnimatedSectionList';
import typeof AnimatedText from './components/AnimatedText';
import typeof AnimatedView from './components/AnimatedView';

const AnimatedMock = require('./AnimatedMock');
const AnimatedImplementation = require('./AnimatedImplementation');

//TODO(T57411659): Remove the bridgeless check when Animated perf regressions are fixed.
const Animated = ((Platform.isTesting ||
(global.RN$Bridgeless && Platform.OS === 'ios')
  ? AnimatedMock
  : AnimatedImplementation): typeof AnimatedMock);

module.exports = {
  get FlatList(): AnimatedFlatList {
    return require('./components/AnimatedFlatList');
  },
  get Image(): AnimatedImage {
    return require('./components/AnimatedImage');
  },
  get ScrollView(): AnimatedScrollView {
    return require('./components/AnimatedScrollView');
  },
  get SectionList(): AnimatedSectionList {
    return require('./components/AnimatedSectionList');
  },
  get Text(): AnimatedText {
    return require('./components/AnimatedText');
  },
  get View(): AnimatedView {
    return require('./components/AnimatedView');
  },
  ...Animated,
};

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 */

'use strict';

import Platform from '../../Utilities/Platform';
import typeof AnimatedFlatList from './components/AnimatedFlatList';
import typeof AnimatedImage from './components/AnimatedImage';
import typeof AnimatedScrollView from './components/AnimatedScrollView';
import typeof AnimatedSectionList from './components/AnimatedSectionList';
import typeof AnimatedText from './components/AnimatedText';
import typeof AnimatedView from './components/AnimatedView';

const AnimatedMock = require('./AnimatedMock');
const AnimatedImplementation = require('./AnimatedImplementation');

const Animated = ((Platform.isTesting
  ? AnimatedMock
  : AnimatedImplementation): typeof AnimatedMock);

module.exports = {
  get FlatList(): AnimatedFlatList {
    return require('./components/AnimatedFlatList');
  },
  get Image(): AnimatedImage {
    return require('./components/AnimatedImage');
  },
  get ScrollView(): AnimatedScrollView {
    return require('./components/AnimatedScrollView');
  },
  get SectionList(): AnimatedSectionList {
    return require('./components/AnimatedSectionList');
  },
  get Text(): AnimatedText {
    return require('./components/AnimatedText');
  },
  get View(): AnimatedView {
    return require('./components/AnimatedView');
  },
  ...Animated,
};

Enable animations in bridgeless mode on iOS


/* global describe, it */
'use strict';

describe('tweet-baker', function () {

    it('should be a function');
    it('should not throw when called with valid arguments');
    it('should throw when called with a non-object tweet argument');
    it('should throw when tweet.text is a non-string');
    it('should throw when tweet.entities is a non-object');
    it('should return the expected HTML');

    describe('.make()', function () {

        it('should be a function');
        it('should not throw when called with no arguments');
        it('should not throw when called with an object templates argument');
        it('should throw when called with an unempty, non-object templates argument');
        it('should return a function');

        describe('[returned function]', function () {

            it('should not throw when called with valid arguments');
            it('should throw when called with a non-object tweet argument');
            it('should throw when tweet.text is a non-string');
            it('should throw when tweet.entities is a non-object');
            it('should return the expected HTML');

        });

    });

});

Add some pending tests to describe the API


import babel from 'rollup-plugin-babel';

export default {
  entry: 'src/index.js',
  sourceMap: true,
  plugins: [
    babel({
      runtimeHelpers: true,
    }),
  ],
  external: [
    'fs',
    'path',
    'rollup-pluginutils',
    'source-map-resolve',
    'babel-runtime/regenerator',
    'babel-runtime/core-js/promise',
    'babel-runtime/helpers/asyncToGenerator',
  ],
  targets: [
    { dest: 'dist/rollup-plugin-sourcemaps.js', format: 'cjs' },
    { dest: 'dist/rollup-plugin-sourcemaps.esm.js', format: 'es6' },
  ],
};

import babel from 'rollup-plugin-babel';

export default {
  entry: 'src/index.js',
  sourceMap: true,
  plugins: [
    babel({
      runtimeHelpers: true,
    }),
  ],
  external: [
    'fs',
    'rollup-pluginutils',
    'source-map-resolve',
    'babel-runtime/regenerator',
    'babel-runtime/core-js/promise',
    'babel-runtime/helpers/asyncToGenerator',
  ],
  targets: [
    { dest: 'dist/rollup-plugin-sourcemaps.js', format: 'cjs' },
    { dest: 'dist/rollup-plugin-sourcemaps.esm.js', format: 'es6' },
  ],
};

Remove path lib from external modules


import commonjs from '@rollup/plugin-commonjs';
import glslify from 'rollup-plugin-glslify';
import resolve from '@rollup/plugin-node-resolve';
import copy from "rollup-plugin-copy";

export default {
    input: ['source/gltf-sample-viewer.js'],
    external: ['gl-matrix', 'axios', 'jpeg-js', 'fast-png'],
    output: [
        {
            file: 'dist/gltf-viewer.js',
            format: 'cjs',
            sourcemap: true
        },
        {
            file: 'dist/gltf-viewer.module.js',
            format: 'esm',
            sourcemap: true,
        }
    ],
    plugins: [
        glslify(),
        resolve({
            browser: true,
            preferBuiltins: false
        }),
        commonjs({
            include: 'node_modules/**'
        }),
        copy({
            targets: [
                {
                    src: [
                        "assets/images/lut_charlie.png",
                        "assets/images/lut_ggx.png",
                        "assets/images/lut_sheen_E.png",
                    ], dest: "dist/assets"
                },
                { src: ["source/libs/*", "!source/libs/hdrpng.js"], dest: "dist/libs" }
            ]
        })
    ]
};

import commonjs from '@rollup/plugin-commonjs';
import glslify from 'rollup-plugin-glslify';
import resolve from '@rollup/plugin-node-resolve';
import copy from "rollup-plugin-copy";

export default {
    input: ['source/gltf-sample-viewer.js'],
    output: [
        {
            file: 'dist/gltf-viewer.js',
            format: 'cjs',
            sourcemap: true
        },
        {
            file: 'dist/gltf-viewer.module.js',
            format: 'esm',
            sourcemap: true,
        }
    ],
    plugins: [
        glslify(),
        resolve({
            browser: true,
            preferBuiltins: false
        }),
        commonjs({
            include: 'node_modules/**'
        }),
        copy({
            targets: [
                {
                    src: [
                        "assets/images/lut_charlie.png",
                        "assets/images/lut_ggx.png",
                        "assets/images/lut_sheen_E.png",
                    ], dest: "dist/assets"
                },
                { src: ["source/libs/*", "!source/libs/hdrpng.js"], dest: "dist/libs" }
            ]
        })
    ]
};

Remove external in top level rollup


/* eslint-env es6 */
/* global THREE */
/* exported BallTurret */

class BallTurret extends THREE.Mesh {
  constructor() {
    const geometry = new THREE.Geometry();

    const ballGeometry = new THREE.IcosahedronGeometry(4, 1);

    const barrelRadius = 0.5;
    const barrelHeight = 8;
    const barrelOffset = 1;

    const barrelGeometry = new THREE.CylinderGeometry(
      barrelRadius,
      barrelRadius,
      barrelHeight
    ).translate(0, barrelHeight / 2, 0);

    const leftBarrelGeometry = barrelGeometry
      .clone()
      .translate(-barrelOffset, 0, 0);

    const rightBarrelGeometry = barrelGeometry
      .clone()
      .translate(barrelOffset, 0, 0);

    geometry.merge(ballGeometry);
    geometry.merge(leftBarrelGeometry);
    geometry.merge(rightBarrelGeometry);

    super(
      geometry,
      new THREE.MeshStandardMaterial({ shading: THREE.FlatShading })
    );
  }
}

/* eslint-env es6 */
/* global THREE */
/* exported BallTurret */

class BallTurret extends THREE.Mesh {
  constructor() {
    const geometry = new THREE.Geometry();

    const ballGeometry = new THREE.IcosahedronGeometry(4, 1);

    const barrelRadius = 0.5;
    const barrelHeight = 8;
    const barrelOffset = 1;

    const barrelGeometry = new THREE.CylinderGeometry(
      barrelRadius,
      barrelRadius,
      barrelHeight
    )
      .translate(0, -barrelHeight / 2, 0)
      .rotateX(-Math.PI / 2);

    const leftBarrelGeometry = barrelGeometry
      .clone()
      .translate(-barrelOffset, 0, 0);

    const rightBarrelGeometry = barrelGeometry
      .clone()
      .translate(barrelOffset, 0, 0);

    geometry.merge(ballGeometry);
    geometry.merge(leftBarrelGeometry);
    geometry.merge(rightBarrelGeometry);

    super(
      geometry,
      new THREE.MeshStandardMaterial({ shading: THREE.FlatShading })
    );
  }
}

Fix orientation & direction of BallTurret gun barrels.


(function () {
    "use strict";

    function UnpublishController($scope, $controller, editorState, service) {
        var vm = this;
        angular.extend(this, $controller('Umbraco.Overlays.UnpublishController', { $scope: $scope }));

        vm.loading = true;
        vm.relations = [];

        function init() {
            service.checkRelations(editorState.current.udi).then(function(data) {
                vm.relations = data;
                vm.loading = false;
            });

        }

        init();
    }

    angular.module("umbraco").controller("Our.Umbraco.Nexu.Controllers.UnpublishController",
        [
            '$scope',
            '$controller',
            'editorState',
            'Our.Umbraco.Nexu.Services.RelationCheckServive',
        UnpublishController]);

})();

(function () {
    "use strict";

    function UnpublishController($scope, $controller, editorState, service) {
        var vm = this;
        angular.extend(this, $controller('Umbraco.Overlays.UnpublishController', { $scope: $scope }));

        vm.loading = true;
        vm.relations = [];
        vm.showLanguageColumn = false;
        $scope.model.disableSubmitButton = true;

        function init() {
            service.checkRelations(editorState.current.udi).then(function (data) {
                $scope.model.disableSubmitButton = false;
                vm.relations = data;
                vm.loading = false;
            });

        }

        init();
    }

    angular.module("umbraco").controller("Our.Umbraco.Nexu.Controllers.UnpublishController",
        [
            '$scope',
            '$controller',
            'editorState',
            'Our.Umbraco.Nexu.Services.RelationCheckServive',
        UnpublishController]);

})();

Disable submit button while loading relations in unpublish dialog


var test = require('tap').test;
var browserify = require('browserify');

var vm = require('vm');
var path = require('path');

test('sync string encoding', function (t) {
    t.plan(2);
    var b = browserify(__dirname + '/files/buffer.js');
    b.require('buffer', { expose: 'buffer' });
    b.transform(path.dirname(__dirname));
    b.bundle(function (err, src) {
        if (err) t.fail(err);
        var context = {
            setTimeout: setTimeout,
            console: { log: log }
        };
        var buffers = [];
        vm.runInNewContext(src, context);

        t.ok(context.require('buffer').Buffer.isBuffer(buffers[0]), 'isBuffer');
        t.equal(buffers[0].toString('utf8'), '<b>beep boop</b>\n');

        function log (msg) { buffers.push(msg) }
    });
});

var test = require('tap').test;
var browserify = require('browserify');

var vm = require('vm');
var path = require('path');

if (!ArrayBuffer.isView) ArrayBuffer.isView = function () { return false; };

test('sync string encoding', function (t) {
    t.plan(2);
    var b = browserify(__dirname + '/files/buffer.js');
    b.require('buffer', { expose: 'buffer' });
    b.transform(path.dirname(__dirname));
    b.bundle(function (err, src) {
        if (err) t.fail(err);
        var context = {
            setTimeout: setTimeout,
            console: { log: log },
            ArrayBuffer: ArrayBuffer,
            Uint8Array: Uint8Array,
            DataView: DataView
        };
        var buffers = [];
        vm.runInNewContext(src, context);

        t.ok(context.require('buffer').Buffer.isBuffer(buffers[0]), 'isBuffer');
        t.equal(buffers[0].toString('utf8'), '<b>beep boop</b>\n');

        function log (msg) { buffers.push(msg) }
    });
});

Fix Buffer tests in <= 0.10


const path = require('path');
const expect = require('chai').expect;
const findProject = require('../../src/config/ios/findProject');
const mockFs = require('mock-fs');
const projects = require('../fixtures/projects');

describe('ios::findProject', () => {

  before(() => {
    mockFs({ testDir: projects });
  });

  it('should return path to xcodeproj if found', () => {
    const userConfig = {};

    mockFs(projects.flat);

    expect(findProject('')).to.contain('.xcodeproj');
  });

  it('should ignore xcodeproj from example folders', () => {
    const userConfig = {};

    mockFs({
      examples: projects.flat,
      Examples: projects.flat,
      App: projects.flat,
    });

    expect(findProject('').toLowerCase()).to.not.contain('examples');
  });

  it('should ignore xcodeproj from test folders at any level', () => {
    const userConfig = {};

    mockFs({
      test: projects.flat,
      IntegrationTests: projects.flat,
      tests: projects.flat,
      app: {
        tests: projects.flat,
        src: projects.flat,
      },
    });

    expect(findProject('').toLowerCase()).to.not.contain('tests');
  });

  afterEach(() => {
    mockFs.restore();
  });

});

const path = require('path');
const expect = require('chai').expect;
const findProject = require('../../src/config/ios/findProject');
const mockFs = require('mock-fs');
const projects = require('../fixtures/projects');

describe('ios::findProject', () => {

  before(() => {
    mockFs({ testDir: projects });
  });

  it('should return path to xcodeproj if found', () => {
    const userConfig = {};

    mockFs(projects.flat);

    expect(findProject('')).to.contain('.xcodeproj');
  });

  it('should ignore xcodeproj from example folders', () => {
    const userConfig = {};

    mockFs({
      examples: projects.flat,
      Examples: projects.flat,
      App: projects.flat,
    });

    expect(findProject('').toLowerCase()).to.not.contain('examples');
  });

  it('should ignore xcodeproj from test folders at any level', () => {
    const userConfig = {};

    mockFs({
      test: projects.flat,
      IntegrationTests: projects.flat,
      tests: projects.flat,
      app: {
        tests: projects.flat,
        src: projects.flat,
      },
    });

    expect(findProject('').toLowerCase()).to.not.contain('test');
  });

  afterEach(() => {
    mockFs.restore();
  });

});

Make sure it does not pick as well


"use strict";

var JSHINT = require("jshint").JSHINT,
    assert = require("assert"),
    glob   = require("glob"),
    path   = require("path"),
    fs     = require("fs"),
    _      = require('underscore');

var projectDir = path.normalize(path.join(__dirname, '..'));

var jsFiles = glob.sync(
  "**/*.js",
  { cwd: projectDir }
);

var jshintrc = path.join(projectDir, '.jshintrc');
var jshintConfig = JSON.parse(fs.readFileSync(jshintrc).toString());

describe("Run jsHint on", function () {

  jsFiles.forEach(function (file) {

    if (/node_modules/.test(file)) {
      return;
    }

    it(file, function () {
      var content = fs.readFileSync(path.join(projectDir, file)).toString();

      // Split the content into lines and replace whitespace only lines with
      // empty strings so that this test behaviour mimics that of the command
      // line tool.
      var lines = _.map(
        content.split(/[\n\r]/),
        function (line) {
          return (/^\s+$/).test(line) ? '' : line;
        }
      );

      var success = JSHINT(
        lines,
        jshintConfig
      );

      var errorMessage = "";
      if (!success) {
        // console.log(JSHINT.errors[0]);
        errorMessage = "line " + JSHINT.errors[0].line + ": " + JSHINT.errors[0].raw;
      }

      assert.ok(success, errorMessage);
    });
  });

});

"use strict";

var JSHINT = require("jshint").JSHINT,
    assert = require("assert"),
    glob   = require("glob"),
    path   = require("path"),
    fs     = require("fs"),
    _      = require('underscore');

var projectDir = path.normalize(path.join(__dirname, '..'));

var jsFiles = glob.sync(
  "**/*.js",
  { cwd: projectDir }
);

var jshintrc = path.join(projectDir, '.jshintrc');
var jshintConfig = JSON.parse(fs.readFileSync(jshintrc).toString());

describe("Run jsHint on", function () {

  jsFiles.forEach(function (file) {

    if (/node_modules/.test(file)) {
      return;
    }

    it(file, function () {
      var content = fs.readFileSync(path.join(projectDir, file)).toString();

      // Split the content into lines and replace whitespace only lines with
      // empty strings so that this test behaviour mimics that of the command
      // line tool.
      var lines = _.map(
        content.split(/[\n\r]/),
        function (line) {
          return (/^\s+$/).test(line) ? '' : line;
        }
      );

      var success = JSHINT(
        lines,
        jshintConfig
      );

      var errorMessage = "";
      if (!success) {
        var error = JSHINT.data().errors[0];
        // console.log(error);
        errorMessage = "line " + error.line + ": " + error.reason;
      }

      assert.ok(success, errorMessage);
    });
  });

});

Return the correctly formatted error message


#!/usr/bin/env node

require('../lib/fin/lib/js.io/packages/jsio')

jsio.path.shared = './lib/fin/js/'
jsio.path.server = './lib/fin/js/'
jsio.path.client = './lib/fin/js/'
jsio.path.fan = '.'

jsio('import client.fin') // makes fin globally accesible
jsio('import fan.sha1')

fin.registerEventHandler('FAN_AUTHENTICATION_RESPONSE', function() {
	process.exit()
})

fin.connect(function() {
	fin.send('FAN_REQUEST_CREATE_USER', { email: process.argv[2], password_hash: fan.sha1('123123') })
})

var sys = require('sys')
require('../lib/fin/fin')

jsio.path.fan = '..'

jsio('import fan.sha1')

var email = process.argv[2]

fin.registerEventHandler('FAN_AUTHENTICATION_RESPONSE', function() {
	sys.puts("Sucessfully create user with email " + email)
	process.exit()
})

fin.connect(function() {
	fin.send('FAN_REQUEST_CREATE_USER', { email: email, password_hash: fan.sha1('123123') })
})

Fix the user creation script


//= requires "editor"

/*
 * Includes the Aspell plugin (kindly ported from FCKeditor by Christian
 * Boisjoli) which provides spell checking without relying on any
 * browser plugins or third-party web services.
 *
 * Note: you must add this to the toolbar as 'SpellCheck' rather than
 * the usual 'SpellChecker' in order to use it.
 *
 * The plugin can be configured in config/ckeditor.php:
 *   aspell.language sets which dictionary to use - by default it uses
 *     Kohana's locale configuration
 *   aspell.program sets the path to the Aspell executable with sensible
 *     defaults for both Windows and Linux
 *
 * Details on where to obtain the original plugin code are in:
 *   vendor/ckplugins/aspell/aspell.txt
 */

CKEDITOR.plugins.addExternal('aspell', "<?php echo url::site('assets/ckplugins/aspell');?>/");
CKEDITOR.config.extraPlugins = 'aspell';

//= requires "editor"

/*
 * Includes the Aspell plugin (kindly ported from FCKeditor by Christian
 * Boisjoli) which provides spell checking without relying on any
 * browser plugins or third-party web services.
 *
 * Note: you must add this to the toolbar as 'SpellCheck' rather than
 * the usual 'SpellChecker' in order to use it.
 *
 * The plugin can be configured in config/ckeditor.php:
 *   aspell.language sets which dictionary to use - by default it uses
 *     Kohana's locale configuration
 *   aspell.program sets the path to the Aspell executable with sensible
 *     defaults for both Windows and Linux
 *
 * Details on where to obtain the original plugin code are in:
 *   vendor/ckplugins/aspell/aspell.txt
 */

CKEDITOR.plugins.addExternal('aspell', "<?php echo url::site('assets/ckplugins/aspell');?>/");
CKEDITOR.config.extraPlugins = (CKEDITOR.config.extraPlugins ? (CKEDITOR.config.extraPlugins + ',') : '' ) + 'aspell';

Make sure Aspell plugin extends rather than overwrites config.extraPlugins


(function (env) {
    "use strict";

    function getInfoBoxData(item) {
        var infoboxData = [{
            heading: 'Ingredients:'
        }];
        for (var i = 1; i <= 15; i++) {
            if(item["strIngredient" + i] !== "") {
                infoboxData.push({
                    label: item["strMeasure" + i] + "" + item["strIngredient" + i]
                });
            }
        }
        return infoboxData;
    }

    env.ddg_spice_drinks = function(api_result){

        if (!api_result || api_result.error) {
            return Spice.failed('drinks');
        }

        var drink = api_result.drinks[0];

        Spice.add({
            id: 'drinks',
            data: drink,
            name: "Drinks",
            meta: {
                sourceUrl:  "http://www.thecocktaildb.com/drink.php?c=" + drink.idDrink,
                sourceName: 'TheCocktailDB'
            },
            normalize: function(item) {
                return {
                    description: item.strInstructions,
                    title: item.strDrink,
                    infoboxData: getInfoBoxData(item)
                };
            },
            templates: {
                group: 'info'
            }
        });
    };
}(this));

(function (env) {
    "use strict";

    function getInfoBoxData(item) {
        var infoboxData = [{
            heading: 'Ingredients:'
        }];
        for (var i = 1; i <= 15; i++) {
            if(item["strIngredient" + i] !== "") {
                infoboxData.push({
                    label: item["strMeasure" + i] + "" + item["strIngredient" + i]
                });
            }
        }
        return infoboxData;
    }

    env.ddg_spice_drinks = function(api_result){

        if (!api_result || api_result.error || !api_result.drinks || api_result.drinks.length === 0) {
            return Spice.failed('drinks');
        }

        var drink = api_result.drinks[0];

        Spice.add({
            id: 'drinks',
            data: drink,
            name: "Drinks",
            meta: {
                sourceUrl:  "http://www.thecocktaildb.com/drink.php?c=" + drink.idDrink,
                sourceName: 'TheCocktailDB'
            },
            normalize: function(item) {
                return {
                    description: item.strInstructions,
                    title: item.strDrink,
                    infoboxData: getInfoBoxData(item)
                };
            },
            templates: {
                group: 'info'
            }
        });
    };
}(this));

Check the response whether null or empty


// js/directives/nw-category-select.directive

(function() {
  "use strict";

  angular.module("NoteWrangler")
    .directive("nwCategorySelect", nwCategorySelectDirective);

  //nwCategorySelectDirective.$inject = ["Category"];

  function nwCategorySelectDirective() {
    return {
      restrict: "E",
      templateUrl: "../templates/directives/nw-category-select.directive.html"
    };
  }
});

// js/directives/nw-category-select.directive

(function() {
  "use strict";

  angular.module("NoteWrangler")
    .directive("nwCategorySelect", nwCategorySelectDirective);

  nwCategorySelectDirective.$inject = ["Category"];

  function nwCategorySelectDirective(Category) {
    return {
      replace: true,
      restrict: "E",
      templateUrl: "../templates/directives/nw-category-select.directive.html",
      link: (scope, element, attrs) => {
        scope.categories = Category.query();
      },
      // need refactoring
      /*controller: ($scope) => {
        this.activeCategory = () => {
          return $scope.activeCategory;
        };

        this.setActiveCategory = (category) => {
          $scope.activeCategory = category;
        };

        return this;
      },*/
      controller: "nwCategorySelectController",
      controllerAs: "nwCategorySelectCtrl"
    };
  }
})();

Edit templateUrl value. Add require, link values. Add nwCategoryItemDirective.. Comment code


/**
 * status.js
 * chd 160218
 *
 * status:
 * function inputs:
 *    1. error.txt array of strings from XMLvalidataion
 *    2. error.txt array of strings from IMGvalidataion
 *    3. error.txt array of strings from Controller_validataion
 * function outputs:
 *    1. errorReport.txt flat file written to a directory or sent to Controller
 *    2. errorLog.txt flat file written to a directory or sent to Controller
 * function purpose:
 *    1. Translate coded input messages to English Language format
 *	 for errorReport.txt
 *    2. Translate coded input messages to errorLog format for errorLog.txt
 *
 *
 *
 *
 * @return {[type]} [description]
 */

Add header comments to top of file.


BEST.module('famous:examples:demos:clickable-square', 'HEAD', {
    tree: 'clickable-square.html',
    behaviors: {
        '#context': {
            'size': [200, 200],
            'position': function(offset) {
                return [offset, offset]
            }
        },
        '#surface': {
            'template': function(count) { return { count: count }; },
            'style' : function(backgroundColor) {
                return {
                    'background-color' : backgroundColor,
                    'cursor' : 'pointer'
                }
            },
            'unselectable': true
        }
    },
    events: {
        '#context': {
            'famous:events:click': function($state, $payload) {
                $state.set('count', $state.get('count') + 1);
                console.log('Click event on context: ', $payload);
            }
        },
        '$public': {
            'hello' : function() {
                console.log('hello!');
            },
            'background-color' : 'setter|camel'
        }
    },
    states: {
        count: 0,
        offset: 0,
        backgroundColor: 'gray'
    }
});

BEST.module('famous:examples:demos:clickable-square', 'HEAD', {
    tree: 'clickable-square.html',
    behaviors: {
        '#context': {
            'size': [200, 200],
            'position': function(offset) {
                return [offset, offset]
            }
        },
        '#surface': {
            'template': function(count) { return { count: count }; },
            'style' : function(backgroundColor) {
                return {
                    'background-color' : backgroundColor,
                    'cursor' : 'pointer'
                }
            },
            'unselectable': true
        }
    },
    events: {
        '#context': {
            'famous:events:click': function($state, $payload) {
                $state.set('count', $state.get('count') + 1);
                console.log('Click event on context: ', $payload);
            }
        },
        '$public': {
            'hello' : function() {
                console.log('hello!');
            },
            'background-color' : 'setter|camel'
        }
    },
    states: {
        count: 0,
        offset: 100,
        backgroundColor: 'gray'
    }
});

Add offset to clickable square


function toggleDisp(){
    var txt = document.getElementById('asText');
    var box = document.getElementById('asBoxed');
    if (txt.style.display == 'none') {
        box.style.display = 'none';
        txt.style.display = 'block';
    }
    else {
        txt.style.display = 'none';
        box.style.display = 'block';
    }
}

function addGraphs(){
    addPie("Code Point Ages", "ages");
    addPie("Block Names", "blocks");
    addBar("Bytes to Encode", "sizes")
}

function addPie(title, data){
    c3.generate({
        bindto: '#'+data,
        data: {
            columns: toGraph[data] ,
            type : 'donut'
        },
        donut: {
            title: title
        }
    });
}

function addBar(title, data){
    c3.generate({
        bindto: '#'+data,
        data: {
            columns: toGraph[data],
            type: 'bar'
        },
        bar: {
            title: title
        },
        axis: {
            x: {
                show: false,
                ratio: 1
            },
            y:{
                tick: {
                    format: function(d){return d + "B"}
                }
            },
            rotated: true
        },
        tooltip: {
            grouped: false,
            format:{
                title: function () { return 'Bytes to encode'}
            }
        },
        grid: {
            x:{
                show:false
            }
        }
    });
}

addGraphs();

function toggleDisp(){
    var txt = document.getElementById('asText');
    var box = document.getElementById('asBoxed');
    if (txt.style.display == 'none') {
        box.style.display = 'none';
        txt.style.display = 'block';
    }
    else {
        txt.style.display = 'none';
        box.style.display = 'block';
    }
}

function addGraphs(){
    addPie("Code Point Ages", "ages");
    addPie("Block Names", "blocks");
    addBar("Bytes to Encode", "sizes")
}

function addPie(title, data){
    c3.generate({
        bindto: '#'+data,
        data: {
            columns: toGraph[data] ,
            type : 'donut'
        },
        donut: {
            title: title,
            label: {
                format: function (value, ratio, id) {
                    return value;
                }
            }
        }
    });
}

function addBar(title, data){
    c3.generate({
        bindto: '#'+data,
        data: {
            columns: toGraph[data],
            type: 'bar'
        },
        bar: {
            title: title
        },
        axis: {
            x: {
                show: false,
                ratio: 1
            },
            y:{
                tick: {
                    format: function(d){return d + "B"}
                }
            },
            rotated: true
        },
        tooltip: {
            grouped: false,
            format:{
                title: function () { return 'Bytes to encode'}
            }
        },
        grid: {
            x:{
                show:false
            }
        }
    });
}

addGraphs();

Make donuts display raw values


var lastField = null;
var currentFillColor = '';   // ???
var changeCounter = 0;       // ???

function setField(element) {
    // element contains the current html element
    if (element.style.backgroundColor !== currentFillColor) {
        element.style.backgroundColor = currentFillColor;
    } else {
        element.style.backgroundColor = '#eee';
    }
    if (lastField === element) {
        ++changeCounter;
    } else {
        changeCounter = 0;
    }
    if (changeCounter === 10) {
        alert('You have clicked 10 times on the field!');
        changeCounter = 0;
    }
    lastField = element;
}

function setFillColor(color) {
    // color should be a string
    var logEntry = document.createElement('p');
    logEntry.innerHTML = 'Color changed';
    logEntry.style.color = currentFillColor = color;
    document.getElementById('Log').appendChild(logEntry);
}

var lastField = null;
var currentFillColor = '';   // ???
var changeCounter = 0;       // ???

function setField(element) {
    // element contains the current html element
    if (element.style.backgroundColor !== currentFillColor) {
        element.style.backgroundColor = currentFillColor;
    } else {
        element.style.backgroundColor = '#eee';
    }
    ++changeCounter;
    if (changeCounter === 10) {
        alert('You have clicked 10 times on the field!');
        changeCounter = 0;
    }
}

function setFillColor(color) {
    // color should be a string
    var logEntry = document.createElement('p');
    logEntry.innerHTML = 'Color changed';
    logEntry.style.color = currentFillColor = color;
    document.getElementById('Log').appendChild(logEntry);
}

Apply proposals by tut2 feedback


import React from 'react';
import TodoItem from './TodoItem';

export default class extends React.Component {
  render() {
    return (

        <button onClick={this.props.onAddTodo}>Add Todo</button>

          <For each="item" of={this.props.todos}>
            <TodoItem
              key={item.id}
              id={item.id}
              value={item.value}
              completed={item.completed}
              onEdit={this.props.onEditTodo}
              onDelete={this.props.onDeleteTodo}
              onSubmit={this.props.onAddTodo}
            />
          </For>

          {this.props.todos.length}{' '}
          todo{this.props.todos.length > 1 ? 's' : ''}

    );
  }
}

import React from 'react';
import TodoItem from './TodoItem';

export default class extends React.Component {
  render() {
    return (

        <button onClick={this.props.onAddTodo}>Add Todo</button>

          <For each="item" of={this.props.todos}>
            <TodoItem
              key={item.id}
              id={item.id}
              value={item.value}
              completed={item.completed}
              onEdit={this.props.onEditTodo}
              onDelete={this.props.onDeleteTodo}
              onSubmit={this.props.onAddTodo}
            />
          </For>

          {this.props.todos.length}{' '}
          todo{this.props.todos.length === 1 ? '' : 's'}

    );
  }
}

Fix plural form of todo when there are no todos


// Copyright © 2017 Michael Howell. All rights reserved.
// The following code is covered by the AGPL-3.0 license.

const selfapi = require('selfapi');

const blog = require('../lib/blog');
const log = require('../lib/log');

// API resource to manage Janitor's Discourse-backed news section.
const blogAPI = module.exports = selfapi({
  title: 'Blog'
});

blogAPI.post('/synchronize', {
  title: 'Synchronize Blog',
  description: 'Pull the blog section from Discourse.',

  handler: async (_request, response) => {
    try {
      const { count } = await blog.synchronize();
      log('synchronized blog', { count });
      response.json({ count }, null, 2);
    } catch (error) {
      log('[fail] synchronized blog', error);
      response.statusCode = 500; // Internal Server Error
      response.json({ error: 'Could not synchronize' }, null, 2);
    }
  },

  examples: [{
    response: {
      body: JSON.stringify({ count: 13 }, null, 2)
    }
  }]
});

// Copyright © 2017 Michael Howell. All rights reserved.
// The following code is covered by the AGPL-3.0 license.

const selfapi = require('selfapi');

const blog = require('../lib/blog');
const log = require('../lib/log');

// API resource to manage Janitor's Discourse-backed news section.
const blogAPI = module.exports = selfapi({
  title: 'Blog'
});

blogAPI.post('/synchronize', {
  title: 'Synchronize Blog',
  description: 'Pull the blog section from Discourse.',

  handler: async (_request, response) => {
    try {
      const { count } = await blog.synchronize();
      log('synchronized blog', { count });
      response.json({ count }, null, 2);
    } catch (error) {
      log('[fail] synchronized blog', error);
      response.statusCode = 500; // Internal Server Error
      response.json({ error: 'Could not synchronize' }, null, 2);
    }
  },

  // FIXME: Re-enable this test once syncing with Discourse doesn't cause frequent errors.
  // This can be achieved by rate-limiting our own sync requests, and/or by using a Discourse token.
  // See https://github.com/JanitorTechnology/janitor/issues/219
  examples: [/* {
    response: {
      body: JSON.stringify({ count: 13 }, null, 2)
    }
  } */]
});

Disable the error-prone Discourse synchronization test to fix Travis.


var Backbone = require('./backbone');
var _ = require('underscore');
var HTTPServer = require('express').HTTPServer;
var middleware = require('..').middleware;

module.exports = Server;
function Server(plugin) {
    HTTPServer.call(this, []);
    this.plugin = plugin;
    this.initialize(plugin);
    this.conclude(plugin);
};

Server.prototype.__proto__ = HTTPServer.prototype;

_.extend(Server.prototype, Backbone.Events, {
    initialize : function(plugin) {},

    conclude: function(plugin) {
        // Add catchall 404 middleware and error handler for root servers.
        if (this.port) {
            this.use(middleware.notFound());
            this.error(middleware.showError());
        // Remove redundant frontmost middleware from each server that will not
        // be a root server. See `express/lib/http.js`.
        } else {
            this.stack.shift();
        }
    },

    port: null,

    start: function(callback) {
        this.port && this.listen(this.port, callback);
        return this;
    },

    toString: function() {
        if (this.port) {
            return '[Server ' + this.constructor.title + ':' + this.address().port + ']';
        } else {
            return '[Server ' + this.constructor.title + ']';
        }
    }
});

Server.augment = Backbone.Router.augment;
Server.extend = Backbone.Router.extend;
Server.toString = function() {
    return '<Server ' + this.title + '>';
};

var Backbone = require('./backbone');
var _ = require('underscore');
var HTTPServer = require('express').HTTPServer;
var middleware = require('..').middleware;

module.exports = Server;
function Server(plugin) {
    HTTPServer.call(this, []);
    this.plugin = plugin;
    this.initialize.apply(this, arguments);
    this.conclude.apply(this, arguments);
};

Server.prototype.__proto__ = HTTPServer.prototype;

_.extend(Server.prototype, Backbone.Events, {
    initialize: function(plugin) {},

    conclude: function(plugin) {
        // Add catchall 404 middleware and error handler for root servers.
        if (this.port) {
            this.use(middleware.notFound());
            this.error(middleware.showError());
        // Remove redundant frontmost middleware from each server that will not
        // be a root server. See `express/lib/http.js`.
        } else {
            this.stack.shift();
        }
    },

    port: null,

    start: function(callback) {
        this.port && this.listen(this.port, callback);
        return this;
    },

    toString: function() {
        if (this.port) {
            return '[Server ' + this.constructor.title + ':' + this.address().port + ']';
        } else {
            return '[Server ' + this.constructor.title + ']';
        }
    }
});

Server.augment = Backbone.Router.augment;
Server.extend = Backbone.Router.extend;
Server.toString = function() {
    return '<Server ' + this.title + '>';
};

Use .apply() in function Server(), to apply whatever number of arguments


var fstream = require('../fstream.js')
var notOpen = false

fstream
  .Writer({
    path: 'path/to/symlink',
    linkpath: './file',
    isSymbolicLink: true,
    mode: '0755' // octal strings supported
  })
  .on('close', function () {
    notOpen = true
    var fs = require('fs')
    var s = fs.lstatSync('path/to/symlink')
    var isSym = s.isSymbolicLink()
    console.log((isSym ? '' : 'not ') + 'ok 1 should be symlink')
    var t = fs.readlinkSync('path/to/symlink')
    var isTarget = t === './file'
    console.log((isTarget ? '' : 'not ') + 'ok 2 should link to ./file')
  })
  .end()

process.on('exit', function () {
  console.log((notOpen ? '' : 'not ') + 'ok 3 should be closed')
})

var fstream = require('../fstream.js')
var notOpen = false

// No symlinks on Windows
if (process.platform !== 'win32') {
  fstream
    .Writer({
      path: 'path/to/symlink',
      linkpath: './file',
      isSymbolicLink: true,
      mode: '0755' // octal strings supported
    })
    .on('close', function () {
      notOpen = true
      var fs = require('fs')
      var s = fs.lstatSync('path/to/symlink')
      var isSym = s.isSymbolicLink()
      console.log((isSym ? '' : 'not ') + 'ok 1 should be symlink')
      var t = fs.readlinkSync('path/to/symlink')
      var isTarget = t === './file'
      console.log((isTarget ? '' : 'not ') + 'ok 2 should link to ./file')
    })
    .end()

  process.on('exit', function () {
    console.log((notOpen ? '' : 'not ') + 'ok 3 should be closed')
  })
}

Disable symlink test on Windows


var babel = require("gulp-babel");
var gulp = require("gulp");
var sourcemaps = require("gulp-sourcemaps");
var plumber = require('gulp-plumber');

gulp.task("default", () =>
    gulp.src("lib/*.js")
        .pipe(plumber())
        .pipe(sourcemaps.init())
        .pipe(babel({
            presets: ['es2015']
        }))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("./")));

gulp.watch('lib/*.js', ['default']);

var babel = require("gulp-babel");
var gulp = require("gulp");
var sourcemaps = require("gulp-sourcemaps");
var plumber = require('gulp-plumber');

gulp.task("default", () =>
    gulp.src("lib/*.js")
        .pipe(plumber())
        .pipe(sourcemaps.init())
        .pipe(babel({
            presets: ['es2015']
        }))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("./")));

gulp.task('watch', () => {
    gulp.watch('lib/*.js', ['default']);
});

Move watch to own task


/* globals define */

define([
  'jquery',
  'backbone',
  'marionette',
  'controllers/event-bus'
], function ($, Backbone, Marionette, EventBus) {
  'use strict';

  var Navigator = Backbone.Model.extend({
    navigate: function (route, trigger) {
      EventBus.trigger('router:nav', {route: route, options: {trigger: trigger}});
    }
  });

  return new Navigator();
});

/* globals define */

define([
  'jquery',
  'backbone',
  'marionette',
  'controllers/event-bus'
], function ($, Backbone, Marionette, EventBus) {
  'use strict';

  var Navigator = Backbone.Model.extend({
    navigate: function (route, trigger) {
      EventBus.trigger('router:nav', {route: route, options: {trigger: trigger}});
    },

    getRoute: function () {
      return Backbone.history.fragment;
    }
  });

  return new Navigator();
});

Add method to retrieve route fragment to Navigator


import React, { Component } from 'react';
const mapKey = process.env.key;

class Place extends Component {
  constructor(props) {
    super(props);

    // this.state = {
    //   place: {}
    // };

    // this.setState({ place: this.props.place })

    console.log('Mapkey:', mapKey);
  }

  render() {
    if (this.props.place.rating !== undefined) {
      return (

{this.props.place.name}

{this.props.place.formatted_address}

          Rating: {this.props.place.rating}

      )
    } else {
      return(

)
    }
  }
}

export default Place;

import React, { Component } from 'react';
const mapKey = process.env.key;

class Place extends Component {
  constructor(props) {
    super(props);

    // this.state = {
    //   place: {}
    // };

    // this.setState({ place: this.props.place })

    console.log('Mapkey:', mapKey);
  }

  price(n) {
    let money = "";
    for (var i=0; i < n; i++){
      money += "$";
    }
    return money;
  }

  render() {
    if (this.props.place.rating !== undefined) {
      return (

{this.props.place.name}

{this.props.place.formatted_address}

          Rating: {this.props.place.rating}

Price: {this.price(this.props.place.price_level)}

      )
    } else {
      return(

)
    }
  }
}

export default Place;

ADD map AND price to search result display