<commit_before>'use strict'

var Boom = require('boom')
var _ = require('lodash')
var pluginName = 'pluginJsonapi'
var utilities = require('./utilities')

// some paths sinply don't want to be jsonapi'd
var ignorePaths = [
  '/', // Lout documentation and AWS healthcheck
  '/css/{path*}' // Lout documentation
]

// certain status codes shouldn't be jsonapi'd
var ignoreStatusCodes = [
  201, // Created (used on POST)
  204 // No Content (used on DELETE)
]

var proxyableKeys = [
  'contentVersion',
  'lang',
  'ticketRates'
]

var pluginJsonApi = module.exports = {}

pluginJsonApi.errorHandler = function errorHandler (request, reply, error) {
  request.log(['error', __filename, pluginName], {
    error: error.toString(),
    data: request.data
  })

  return reply(Boom.internal(error))
}

pluginJsonApi.alsoMakeItSo = function alsoMakeItSo (request, reply) {
 // eslint-disable-line consistent-return

  try {
    // If there was no error and it's not one of our ignoredPaths or ignored status codes, jsonapi it
    if (request.response.isBoom || _.includes(ignorePaths, _.get(request, 'route.path')) || _.includes(ignoreStatusCodes, request.response.statusCode)) {
      // return early
      return reply.continue()
    }

    // Make the response a bit more accessible
    var result = _.get(request, 'response.source')
    // check a bind configuration is present
    if (!_.get(request, 'route.settings.bind.resourceName')) {
      throw new Error('configuration bind.resourceName not found on handler')
    }

    // Get the current resource from the result
    var resources = result[request.route.settings.bind.resourceName]
    if (!resources) {
      // no resources, return early
      return reply.continue()
    }

    var stateObject = {
      request: request,
      resources: resources,
      linked: result.linked,
      proxyableKeys: proxyableKeys
    }

    /*
     * Flow
     * collectIncludes() - store the includes for later
     * getSubResources() - get the sub resources which will be linked
     * getResources() - get the resources
     * resolveLinkedData() - resolve the sub resources which we have collected previously
     */
    utilities.collectIncludes(stateObject)
    .then(utilities.collectProxyableValues)
    .then(utilities.getSubResources)
    .then(utilities.getResources)
    .then(utilities.resolveLinkedData)
    .then(function () {
      // all done
      return reply.continue()
    })
    .fail(function (error) {
      return pluginJsonApi.errorHandler(request, reply, error)
    })
    .done()
  } catch (error) {
    return pluginJsonApi.errorHandler(request, reply, error)
  }
}

/**
* Registers the plugin when the hapi server bootstraps - follows the convention for Hapi.js plugins
* @param {object} server - the hapi plugin object
* @param {object} options - any initial options needed to set up the plugin
* @param {function} next - the next plugin to be set up
* @returns {undefined} Nothing - calls the next plugin
*/
pluginJsonApi.register = function (server, options, next) {
  function makeItSo (request, reply) {
    return pluginJsonApi.alsoMakeItSo(request, reply)
  }

  // Extend the onPreResponse for EVERY request through the api
  server.ext('onPreResponse', makeItSo)

  // In order to test this functionality it needs to be exposed on the server.
  if (process.env.NODE_ENV === 'test') {
    server.expose('makeItSo', makeItSo)
  }

  next()
}

pluginJsonApi.register.attributes = {
  pkg: require('../package.json')
}
<commit_msg>correct plugin archetcture for hapi17<commit_after>'use strict'

var Boom = require('boom')
var _ = require('lodash')
var pluginName = 'pluginJsonapi'
var utilities = require('./utilities')

// some paths sinply don't want to be jsonapi'd
var ignorePaths = [
  '/', // Lout documentation and AWS healthcheck
  '/css/{path*}' // Lout documentation
]

// certain status codes shouldn't be jsonapi'd
var ignoreStatusCodes = [
  201, // Created (used on POST)
  204 // No Content (used on DELETE)
]

var proxyableKeys = [
  'contentVersion',
  'lang',
  'ticketRates'
]

var pluginJsonApi = {}

pluginJsonApi.errorHandler = function errorHandler (request, reply, error) {
  request.log(['error', __filename, pluginName], {
    error: error.toString(),
    data: request.data
  })

  return reply(Boom.internal(error))
}

pluginJsonApi.alsoMakeItSo = function alsoMakeItSo (request, reply) {
 // eslint-disable-line consistent-return

  try {
    // If there was no error and it's not one of our ignoredPaths or ignored status codes, jsonapi it
    if (request.response.isBoom || _.includes(ignorePaths, _.get(request, 'route.path')) || _.includes(ignoreStatusCodes, request.response.statusCode)) {
      // return early
      return reply.continue()
    }

    // Make the response a bit more accessible
    var result = _.get(request, 'response.source')
    // check a bind configuration is present
    if (!_.get(request, 'route.settings.bind.resourceName')) {
      throw new Error('configuration bind.resourceName not found on handler')
    }

    // Get the current resource from the result
    var resources = result[request.route.settings.bind.resourceName]
    if (!resources) {
      // no resources, return early
      return reply.continue()
    }

    var stateObject = {
      request: request,
      resources: resources,
      linked: result.linked,
      proxyableKeys: proxyableKeys
    }

    /*
     * Flow
     * collectIncludes() - store the includes for later
     * getSubResources() - get the sub resources which will be linked
     * getResources() - get the resources
     * resolveLinkedData() - resolve the sub resources which we have collected previously
     */
    utilities.collectIncludes(stateObject)
    .then(utilities.collectProxyableValues)
    .then(utilities.getSubResources)
    .then(utilities.getResources)
    .then(utilities.resolveLinkedData)
    .then(function () {
      // all done
      return reply.continue()
    })
    .fail(function (error) {
      return pluginJsonApi.errorHandler(request, reply, error)
    })
    .done()
  } catch (error) {
    return pluginJsonApi.errorHandler(request, reply, error)
  }
}

/**
* Registers the plugin when the hapi server bootstraps - follows the convention for Hapi.js plugins
* @param {object} server - the hapi plugin object
* @param {object} options - any initial options needed to set up the plugin
* @param {function} next - the next plugin to be set up
* @returns {undefined} Nothing - calls the next plugin
*/
const register = function (server, options) {
  function makeItSo (request, reply) {
    return pluginJsonApi.alsoMakeItSo(request, reply)
  }

  // Extend the onPreResponse for EVERY request through the api
  server.ext('onPreResponse', makeItSo)

  // In order to test this functionality it needs to be exposed on the server.
  if (process.env.NODE_ENV === 'test') {
    server.expose('makeItSo', makeItSo)
  }
}

const pkg = require('../package.json')
const name = pkg['name']
const version = pkg['version']

exports.plugin = {
  register,
  name,
  version,
  pkg
}
<|endoftext|>


<commit_before>var path = require('path');
var p = require('pfork');
var fs = require('fs');
var fse = require('fs-extra');
var EventEmitter = require('events').EventEmitter;
var pluginMgr = new EventEmitter();
var extend = require('util')._extend;
var colors = require('colors/safe');
var comUtil = require('../util');
var util = require('./util');
var config = require('../config');
var getPluginsSync = require('./get-plugins-sync');
var getPlugin = require('./get-plugins');
var rulesMgr = require('../rules');
var RulesMgr = rulesMgr.Rules;
var PLUGIN_MAIN = path.join(__dirname, './load-plugin');
var PLUGIN_URL_RE = /^(?:http|ws)s?:\/\/([\da-z]+)\.local\.whistlejs\.com\//i;
var RULE_VALUE_HEADER = 'x-whistle-rule-value';
var SSL_FLAG_HEADER = 'x-whistle-https';
var INTERVAL = 6000;
var UTF8_OPTIONS = {encoding: 'utf8'};
var plugins = getPluginsSync();
var rules = plugins.rules;
plugins = plugins.plugins;

pluginMgr.on('updateRules', function() {
	rulesMgr.clearAppend();
	Object.keys(rules).sort(function(a, b) {
		return a > b ? 1 : -1;
	}).forEach(function(name) {
		var plugin = rules[name];
		if (plugin._rules && !plugin.rulesMgr) {
			plugin.rulesMgr = new RulesMgr();
			plugin.rulesMgr.parse(plugin._rules, plugin.path);
		}
		rulesMgr.append(plugin.rules, plugin.path);
	});
});
parsePluginRules();
pluginMgr.emit('updateRules', rules);

function parsePluginRules() {
	Object.keys(plugins).forEach(function(name) {
		var plugin = plugins[name];
		if (plugin._rules && !plugin.rulesMgr) {
			plugin.rulesMgr = new RulesMgr();
			plugin.rulesMgr.parse(plugin._rules, plugin.path);
		}
	});
}

function showVerbose(tag, oldData, newData) {
	if (!config.debugMode) {
		return;
	}
	var uninstallData, installData, updateData;
	Object.keys(oldData).forEach(function(name) {
		var oldItem = oldData[name];
		var newItem = newData[name];
		if (!newItem) {
			uninstallData = uninstallData || {};
			uninstallData[name] = oldItem;
		} else if (newItem.path != oldItem.path || newItem.mtime != oldItem.mtime) {
			updateData = updateData || {};
			updateData[name] = newItem;
		}
	});

	Object.keys(newData).forEach(function(name) {
		if (!oldData[name]) {
			installData = installData || {};
			installData[name] = newData[name];
		}
	});

	if (uninstallData || installData || updateData) {
		console.log('\n***********[%s] %s has changed***********', comUtil.formatDate(), tag + 's');
	}

	uninstallData && Object.keys(uninstallData).forEach(function(name) {
		console.log(colors.red('[' + comUtil.formatDate(new Date(uninstallData[name].mtime)) + '] [uninstall ' + tag + '] ' + name.slice(0, -1)));
	});
	installData && Object.keys(installData).forEach(function(name) {
		console.log(colors.green('[' + comUtil.formatDate(new Date(installData[name].mtime)) + '] [install ' + tag + '] ' + name.slice(0, -1)));
	});
	updateData && Object.keys(updateData).forEach(function(name) {
		console.log(colors.yellow('[' + comUtil.formatDate(new Date(updateData[name].mtime)) + '] [update ' + tag + '] ' + name.slice(0, -1)));
	});
}

function readPackages(obj, callback) {
	var _plugins = {};
	var _rules = {};
	var count = 0;
	Object.keys(obj).forEach(function(name) {
		var pkg = plugins[name] || rules[name];
		var newPkg = obj[name];
		if (!pkg || pkg.path != newPkg.path || pkg.mtime != newPkg.mtime) {
			++count;
			fse.readJson(newPkg.pkgPath, function(err, pkg) {
				if (pkg && pkg.version) {
					newPkg.version = pkg.version;
					newPkg.homepage = util.getHomePageFromPackage(pkg);
					var pureRules = !!pkg.pureRules;
					newPkg.pureRules = pureRules;
					if (!pureRules) {
						_plugins[name] = newPkg;
					}
					fs.readFile(path.join(path.join(newPkg.path, 'rules.txt')), UTF8_OPTIONS, function(err, rulesText) {
						rulesText = comUtil.trim(rulesText);
						newPkg.rules = rulesText;
						if (rulesText) {
							_rules[name] = newPkg;
						}
						fs.readFile(path.join(path.join(newPkg.path, '_rules.txt')), UTF8_OPTIONS, function(err, rulesText) {
							newPkg._rules = comUtil.trim(rulesText);
							if (--count <= 0) {
								callback(_plugins, _rules);
							}
						});
					});
				}

			});

		} else {
			if (pkg.pureRules || pkg.rules) {
				_rules[name] = pkg;
			}

			if (!pkg.pureRules) {
				_plugins[name] = pkg;
			}
		}
	});

	if (count <= 0) {
		callback(_plugins, _rules);
	}
}

(function update() {
	setTimeout(function() {
		getPlugin(function(result) {
			readPackages(result, function(_plugins, _rules) {
				var hasUpdateRules;
				var rulesKeys = Object.keys(rules);
				var _rulesKeys = Object.keys(_rules);
				hasUpdateRules = rulesKeys.length != _rulesKeys.length;

				if (!hasUpdateRules) {
					for (var i = 0, len = rulesKeys.length; i < len; i++) {
						var name = rulesKeys[i];
						var rule = rules[name];
						var _rule = _rules[name];
						if (!rule || !_rule || rule.rules != _rule.rules) {
							hasUpdateRules = true;
							break;
						}
					}
				}

				showVerbose('rule', rules, _rules);
				rules = _rules;
				hasUpdateRules && pluginMgr.emit('updateRules', rules);

				var updatePlugins, uninstallPlugins;
				Object.keys(plugins).forEach(function(name) {
					var plugin = plugins[name];
					var newPlugin = _plugins[name];
					if (!newPlugin) {
						uninstallPlugins = uninstallPlugins || {};
						uninstallPlugins[name] = plugin;
					} else if (newPlugin.path != plugin.path || newPlugin.mtime != plugin.mtime) {
						updatePlugins = updatePlugins || {};
						updatePlugins[name] = newPlugin;
					}
				});
				showVerbose('plugin', plugins, _plugins);
				plugins = _plugins;
				uninstallPlugins && pluginMgr.emit('uninstall', uninstallPlugins);
				updatePlugins && pluginMgr.emit('update', updatePlugins);
				parsePluginRules();
				update();
			});
		});
	}, INTERVAL);
})();

pluginMgr.RULE_VALUE_HEADER = RULE_VALUE_HEADER;
pluginMgr.SSL_FLAG_HEADER = SSL_FLAG_HEADER;

pluginMgr.loadPlugin = function(plugin, callback) {
	config.debugMode && console.log(colors.cyan('[' + comUtil.formatDate() + '] [access plugin] ' + plugin.path));
	p.fork({
		script: PLUGIN_MAIN,
		value: plugin.path,
		RULE_VALUE_HEADER: RULE_VALUE_HEADER,
		SSL_FLAG_HEADER: SSL_FLAG_HEADER,
		debugMode: config.debugMode,
		config: config
	}, function(err, ports, child) {
		callback && callback(err, ports, child);
		if (config.debugMode) {
			if (err) {
				console.log(colors.red(err));
			} else if (!child.debugMode) {
				child.debugMode = true;
				child.on('data', function(data) {
					if (data && data.type == 'console.log') {
						console.log('[' + comUtil.formatDate() + '] [plugin] [' + plugin.path.substring(plugin.path.lastIndexOf('.') + 1) + ']', data.message);
					}
				});
				child.sendData({
					type: 'console.log',
					status: 'ready'
				});
			}
		}
	});
};

pluginMgr.stopPlugin = function(plugin) {
	p.kill({
		script: PLUGIN_MAIN,
		value: plugin.path
	}, 10000);
};

pluginMgr.getPlugins = function() {
	return plugins;
};

pluginMgr.getPlugin = function(protocol) {
	return plugins[protocol];
};

pluginMgr.getPluginByRuleUrl = function(ruleUrl) {
	if (!ruleUrl || typeof ruleUrl != 'string') {
		return;
	}
	var index = ruleUrl.indexOf(':');
	return index == -1 ? null : plugins[ruleUrl.substring(0, index + 1)];
};

pluginMgr.getPluginByHomePage = function(url) {
	return PLUGIN_URL_RE.test(url)
				&& plugins[RegExp.$1 + ':'];
};

pluginMgr.getRules = function() {
	return rules;
};

module.exports = pluginMgr;<commit_msg>feat: exports config to plugin<commit_after>var path = require('path');
var p = require('pfork');
var fs = require('fs');
var fse = require('fs-extra');
var EventEmitter = require('events').EventEmitter;
var pluginMgr = new EventEmitter();
var extend = require('util')._extend;
var colors = require('colors/safe');
var comUtil = require('../util');
var util = require('./util');
var config = require('../config');
var getPluginsSync = require('./get-plugins-sync');
var getPlugin = require('./get-plugins');
var rulesMgr = require('../rules');
var RulesMgr = rulesMgr.Rules;
var PLUGIN_MAIN = path.join(__dirname, './load-plugin');
var PLUGIN_URL_RE = /^(?:http|ws)s?:\/\/([\da-z]+)\.local\.whistlejs\.com\//i;
var RULE_VALUE_HEADER = 'x-whistle-rule-value';
var SSL_FLAG_HEADER = 'x-whistle-https';
var INTERVAL = 6000;
var UTF8_OPTIONS = {encoding: 'utf8'};
var plugins = getPluginsSync();
var rules = plugins.rules;
var conf = {};
plugins = plugins.plugins;

Object.keys(config).forEach(function(name) {
	var value = config[name];
	if (typeof value == 'string' || typeof value == 'number') {
		conf[name] = value;
	}
});

pluginMgr.on('updateRules', function() {
	rulesMgr.clearAppend();
	Object.keys(rules).sort(function(a, b) {
		return a > b ? 1 : -1;
	}).forEach(function(name) {
		var plugin = rules[name];
		if (plugin._rules && !plugin.rulesMgr) {
			plugin.rulesMgr = new RulesMgr();
			plugin.rulesMgr.parse(plugin._rules, plugin.path);
		}
		rulesMgr.append(plugin.rules, plugin.path);
	});
});
parsePluginRules();
pluginMgr.emit('updateRules', rules);

function parsePluginRules() {
	Object.keys(plugins).forEach(function(name) {
		var plugin = plugins[name];
		if (plugin._rules && !plugin.rulesMgr) {
			plugin.rulesMgr = new RulesMgr();
			plugin.rulesMgr.parse(plugin._rules, plugin.path);
		}
	});
}

function showVerbose(tag, oldData, newData) {
	if (!config.debugMode) {
		return;
	}
	var uninstallData, installData, updateData;
	Object.keys(oldData).forEach(function(name) {
		var oldItem = oldData[name];
		var newItem = newData[name];
		if (!newItem) {
			uninstallData = uninstallData || {};
			uninstallData[name] = oldItem;
		} else if (newItem.path != oldItem.path || newItem.mtime != oldItem.mtime) {
			updateData = updateData || {};
			updateData[name] = newItem;
		}
	});

	Object.keys(newData).forEach(function(name) {
		if (!oldData[name]) {
			installData = installData || {};
			installData[name] = newData[name];
		}
	});

	if (uninstallData || installData || updateData) {
		console.log('\n***********[%s] %s has changed***********', comUtil.formatDate(), tag + 's');
	}

	uninstallData && Object.keys(uninstallData).forEach(function(name) {
		console.log(colors.red('[' + comUtil.formatDate(new Date(uninstallData[name].mtime)) + '] [uninstall ' + tag + '] ' + name.slice(0, -1)));
	});
	installData && Object.keys(installData).forEach(function(name) {
		console.log(colors.green('[' + comUtil.formatDate(new Date(installData[name].mtime)) + '] [install ' + tag + '] ' + name.slice(0, -1)));
	});
	updateData && Object.keys(updateData).forEach(function(name) {
		console.log(colors.yellow('[' + comUtil.formatDate(new Date(updateData[name].mtime)) + '] [update ' + tag + '] ' + name.slice(0, -1)));
	});
}

function readPackages(obj, callback) {
	var _plugins = {};
	var _rules = {};
	var count = 0;
	Object.keys(obj).forEach(function(name) {
		var pkg = plugins[name] || rules[name];
		var newPkg = obj[name];
		if (!pkg || pkg.path != newPkg.path || pkg.mtime != newPkg.mtime) {
			++count;
			fse.readJson(newPkg.pkgPath, function(err, pkg) {
				if (pkg && pkg.version) {
					newPkg.version = pkg.version;
					newPkg.homepage = util.getHomePageFromPackage(pkg);
					var pureRules = !!pkg.pureRules;
					newPkg.pureRules = pureRules;
					if (!pureRules) {
						_plugins[name] = newPkg;
					}
					fs.readFile(path.join(path.join(newPkg.path, 'rules.txt')), UTF8_OPTIONS, function(err, rulesText) {
						rulesText = comUtil.trim(rulesText);
						newPkg.rules = rulesText;
						if (rulesText) {
							_rules[name] = newPkg;
						}
						fs.readFile(path.join(path.join(newPkg.path, '_rules.txt')), UTF8_OPTIONS, function(err, rulesText) {
							newPkg._rules = comUtil.trim(rulesText);
							if (--count <= 0) {
								callback(_plugins, _rules);
							}
						});
					});
				}

			});

		} else {
			if (pkg.pureRules || pkg.rules) {
				_rules[name] = pkg;
			}

			if (!pkg.pureRules) {
				_plugins[name] = pkg;
			}
		}
	});

	if (count <= 0) {
		callback(_plugins, _rules);
	}
}

(function update() {
	setTimeout(function() {
		getPlugin(function(result) {
			readPackages(result, function(_plugins, _rules) {
				var hasUpdateRules;
				var rulesKeys = Object.keys(rules);
				var _rulesKeys = Object.keys(_rules);
				hasUpdateRules = rulesKeys.length != _rulesKeys.length;

				if (!hasUpdateRules) {
					for (var i = 0, len = rulesKeys.length; i < len; i++) {
						var name = rulesKeys[i];
						var rule = rules[name];
						var _rule = _rules[name];
						if (!rule || !_rule || rule.rules != _rule.rules) {
							hasUpdateRules = true;
							break;
						}
					}
				}

				showVerbose('rule', rules, _rules);
				rules = _rules;
				hasUpdateRules && pluginMgr.emit('updateRules', rules);

				var updatePlugins, uninstallPlugins;
				Object.keys(plugins).forEach(function(name) {
					var plugin = plugins[name];
					var newPlugin = _plugins[name];
					if (!newPlugin) {
						uninstallPlugins = uninstallPlugins || {};
						uninstallPlugins[name] = plugin;
					} else if (newPlugin.path != plugin.path || newPlugin.mtime != plugin.mtime) {
						updatePlugins = updatePlugins || {};
						updatePlugins[name] = newPlugin;
					}
				});
				showVerbose('plugin', plugins, _plugins);
				plugins = _plugins;
				uninstallPlugins && pluginMgr.emit('uninstall', uninstallPlugins);
				updatePlugins && pluginMgr.emit('update', updatePlugins);
				parsePluginRules();
				update();
			});
		});
	}, INTERVAL);
})();

pluginMgr.RULE_VALUE_HEADER = RULE_VALUE_HEADER;
pluginMgr.SSL_FLAG_HEADER = SSL_FLAG_HEADER;

pluginMgr.loadPlugin = function(plugin, callback) {
	config.debugMode && console.log(colors.cyan('[' + comUtil.formatDate() + '] [access plugin] ' + plugin.path));
	p.fork({
		script: PLUGIN_MAIN,
		value: plugin.path,
		RULE_VALUE_HEADER: RULE_VALUE_HEADER,
		SSL_FLAG_HEADER: SSL_FLAG_HEADER,
		debugMode: config.debugMode,
		config: conf
	}, function(err, ports, child) {
		callback && callback(err, ports, child);
		if (config.debugMode) {
			if (err) {
				console.log(colors.red(err));
			} else if (!child.debugMode) {
				child.debugMode = true;
				child.on('data', function(data) {
					if (data && data.type == 'console.log') {
						console.log('[' + comUtil.formatDate() + '] [plugin] [' + plugin.path.substring(plugin.path.lastIndexOf('.') + 1) + ']', data.message);
					}
				});
				child.sendData({
					type: 'console.log',
					status: 'ready'
				});
			}
		}
	});
};

pluginMgr.stopPlugin = function(plugin) {
	p.kill({
		script: PLUGIN_MAIN,
		value: plugin.path
	}, 10000);
};

pluginMgr.getPlugins = function() {
	return plugins;
};

pluginMgr.getPlugin = function(protocol) {
	return plugins[protocol];
};

pluginMgr.getPluginByRuleUrl = function(ruleUrl) {
	if (!ruleUrl || typeof ruleUrl != 'string') {
		return;
	}
	var index = ruleUrl.indexOf(':');
	return index == -1 ? null : plugins[ruleUrl.substring(0, index + 1)];
};

pluginMgr.getPluginByHomePage = function(url) {
	return PLUGIN_URL_RE.test(url)
				&& plugins[RegExp.$1 + ':'];
};

pluginMgr.getRules = function() {
	return rules;
};

module.exports = pluginMgr;<|endoftext|>


<commit_before>
var basal = require('./basal');
var targets = require('./targets');
var isf = require('./isf');
var carb_ratios = require('./carbs');
var _ = require('lodash');

function defaults ( ) {
  var profile = {
    max_iob: 0 // if max_iob is not provided, will default to zero
    , max_daily_safety_multiplier: 3
    , current_basal_safety_multiplier: 4
    , autosens_max: 1.2
    , autosens_min: 0.7
    , rewind_resets_autosens: true // reset autosensitivity to neutral for awhile after each pump rewind
    // , autosens_adjust_targets: false // when autosens detects sensitivity/resistance, also adjust BG target accordingly
    , high_temptarget_raises_sensitivity: false // raise sensitivity for temptargets >= 111.  synonym for exercise_mode
    , low_temptarget_lowers_sensitivity: false // lower sensitivity for temptargets <= 99.
    , sensitivity_raises_target: true // raise BG target when autosens detects sensitivity
    , resistance_lowers_target: false // lower BG target when autosens detects resistance
    , adv_target_adjustments: false // lower target automatically when BG and eventualBG are high
    , exercise_mode: false // when true, > 105 mg/dL high temp target adjusts sensitivityRatio for exercise_mode. This majorly changes the behavior of high temp targets from before. synonmym for high_temptarget_raises_sensitivity
    , half_basal_exercise_target: 160 // when temptarget is 160 mg/dL *and* exercise_mode=true, run 50% basal at this level (120 = 75%; 140 = 60%)
    // create maxCOB and default it to 120 because that's the most a typical body can absorb over 4 hours.
    // (If someone enters more carbs or stacks more; OpenAPS will just truncate dosing based on 120.
    // Essentially, this just limits AMA/SMB as a safety cap against excessive COB entry)
    , maxCOB: 120
    , wide_bg_target_range: false // by default use only the low end of the pump's BG target range as OpenAPS target
    // by default the higher end of the target range is used only for avoiding bolus wizard overcorrections
    // use wide_bg_target_range: true to force neutral temps over a wider range of eventualBGs
    , skip_neutral_temps: false // if true, don't set neutral temps
    , unsuspend_if_no_temp: false // if true, pump will un-suspend after a zero temp finishes
    , bolussnooze_dia_divisor: 2 // bolus snooze decays after 1/2 of DIA
    , min_5m_carbimpact: 8 // mg/dL per 5m (8 mg/dL/5m corresponds to 24g/hr at a CSF of 4 mg/dL/g (x/5*60/4))
    , autotune_isf_adjustmentFraction: 1.0 // keep autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF.  1.0 allows full adjustment, 0 is no adjustment from pump ISF.
    , remainingCarbsFraction: 1.0 // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption
    , remainingCarbsCap: 90 // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption
    // WARNING: use SMB with caution: it can and will automatically bolus up to max_iob worth of extra insulin
    , enableUAM: true // enable detection of unannounced meal carb absorption
    , A52_risk_enable: false
    , enableSMB_with_COB: false // enable supermicrobolus while COB is positive
    , enableSMB_with_temptarget: false // enable supermicrobolus for eating soon temp targets
    // *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
    // xDrip+, LimiTTer, etc. do not properly filter out high-noise SGVs
    // Using SMB overnight with such data sources risks causing a dangerous overdose of insulin
    // if the CGM sensor reads falsely high and doesn't come down as actual BG does
    , enableSMB_always: false // always enable supermicrobolus (unless disabled by high temptarget)
    , enableSMB_after_carbs: false // enable supermicrobolus for 6h after carbs, even with 0 COB
    // *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
    , allowSMB_with_high_temptarget: false // allow supermicrobolus (if otherwise enabled) even with high temp targets
    , maxSMBBasalMinutes: 30 // maximum minutes of basal that can be delivered as a single SMB with uncovered COB
    , curve: "rapid-acting" // change this to "ultra-rapid" for Fiasp, or "bilinear" for old curve
    , useCustomPeakTime: false // allows changing insulinPeakTime
    , insulinPeakTime: 75 // number of minutes after a bolus activity peaks.  defaults to 55m for Fiasp if useCustomPeakTime: false
    , carbsReqThreshold: 1 // grams of carbsReq to trigger a pushover
    , offline_hotspot: false // enabled an offline-only local wifi hotspot if no Internet available
    , noisyCGMTargetMultiplier: 1.3 // increase target by this amount when looping off raw/noisy CGM data
    // TODO: make maxRaw a preference here usable by oref0-raw in myopenaps-cgm-loop
    //, maxRaw: 200 // highest raw/noisy CGM value considered safe to use for looping
  };
  return profile;
}

function displayedDefaults () {
    var allDefaults = defaults();
    var profile = { };

    profile.max_iob = allDefaults.max_iob;
    profile.max_daily_safety_multiplier = allDefaults.max_daily_safety_multiplier;
    profile.current_basal_safety_multiplier= allDefaults.current_basal_safety_multiplier;
    profile.autosens_max = allDefaults.autosens_max;
    profile.autosens_min = allDefaults.autosens_min;
    profile.rewind_resets_autosens = allDefaults.rewind_resets_autosens;
    profile.adv_target_adjustments = allDefaults.adv_target_adjustments;
    profile.exercise_mode = allDefaults.exercise_mode;
    profile.wide_bg_target_range = allDefaults.wide_bg_target_range;
    profile.sensitivity_raises_target = allDefaults.sensitivity_raises_target;
    profile.unsuspend_if_no_temp = allDefaults.unsuspend_if_no_temp;
    profile.enableSMB_with_COB = allDefaults.enableSMB_with_COB;
    profile.enableSMB_with_temptarget = allDefaults.enableSMB_with_temptarget;
    profile.enableUAM = allDefaults.enableUAM;
    profile.curve = allDefaults.curve;
    profile.offline_hotspot = allDefaults.offline_hotspot;

    console.error(profile);
    return profile
}

function generate (inputs, opts) {
  var profile = opts && opts.type ? opts : defaults( );

  // check if inputs has overrides for any of the default prefs
  // and apply if applicable
  for (var pref in profile) {
    if (inputs.hasOwnProperty(pref)) {
      profile[pref] = inputs[pref];
    }
  }

  var pumpsettings_data = inputs.settings;
  if (inputs.settings.insulin_action_curve > 1) {
    profile.dia =  pumpsettings_data.insulin_action_curve;
  } else {
    console.error('DIA of', profile.dia, 'is not supported');
    return -1;
  }

  if (inputs.model) {
    profile.model = inputs.model;
  }
  profile.skip_neutral_temps = inputs.skip_neutral_temps;

  profile.current_basal = basal.basalLookup(inputs.basals);
  profile.basalprofile = inputs.basals;

  _.forEach(profile.basalprofile, function(basalentry) {
    basalentry.rate = +(Math.round(basalentry.rate + "e+3")  + "e-3");
  });

  profile.max_daily_basal = basal.maxDailyBasal(inputs);
  profile.max_basal = basal.maxBasalLookup(inputs);
  if (profile.current_basal === 0) {
    console.error("current_basal of",profile.current_basal,"is not supported");
    return -1;
  }
  if (profile.max_daily_basal === 0) {
    console.error("max_daily_basal of",profile.max_daily_basal,"is not supported");
    return -1;
  }
  if (profile.max_basal < 0.1) {
    console.error("max_basal of",profile.max_basal,"is not supported");
    return -1;
  }

  var range = targets.bgTargetsLookup(inputs, profile);
  profile.out_units = inputs.targets.user_preferred_units;
  profile.min_bg = Math.round(range.min_bg);
  profile.max_bg = Math.round(range.max_bg);
  profile.bg_targets = inputs.targets;

  _.forEach(profile.bg_targets.targets, function(bg_entry) {
    bg_entry.high = Math.round(bg_entry.high);
    bg_entry.low = Math.round(bg_entry.low);
    bg_entry.min_bg = Math.round(bg_entry.min_bg);
    bg_entry.max_bg = Math.round(bg_entry.max_bg);
  });

  delete profile.bg_targets.raw;

  profile.temptargetSet = range.temptargetSet;
  profile.sens = isf.isfLookup(inputs.isf);
  profile.isfProfile = inputs.isf;
  if (profile.sens < 5) {
    console.error("ISF of",profile.sens,"is not supported");
    return -1;
  }
  if (typeof(inputs.carbratio) != "undefined") {
    profile.carb_ratio = carb_ratios.carbRatioLookup(inputs, profile);
    profile.carb_ratios = inputs.carbratio;
  } else {
    console.error("Profile wasn't given carb ratio data, cannot calculate carb_ratio");
  }
  return profile;
}

generate.defaults = defaults;
generate.displayedDefaults = displayedDefaults;
exports = module.exports = generate;

<commit_msg>Fix suspend zeros iob preference (#918)<commit_after>
var basal = require('./basal');
var targets = require('./targets');
var isf = require('./isf');
var carb_ratios = require('./carbs');
var _ = require('lodash');

function defaults ( ) {
  var profile = {
    max_iob: 0 // if max_iob is not provided, will default to zero
    , max_daily_safety_multiplier: 3
    , current_basal_safety_multiplier: 4
    , autosens_max: 1.2
    , autosens_min: 0.7
    , rewind_resets_autosens: true // reset autosensitivity to neutral for awhile after each pump rewind
    // , autosens_adjust_targets: false // when autosens detects sensitivity/resistance, also adjust BG target accordingly
    , high_temptarget_raises_sensitivity: false // raise sensitivity for temptargets >= 111.  synonym for exercise_mode
    , low_temptarget_lowers_sensitivity: false // lower sensitivity for temptargets <= 99.
    , sensitivity_raises_target: true // raise BG target when autosens detects sensitivity
    , resistance_lowers_target: false // lower BG target when autosens detects resistance
    , adv_target_adjustments: false // lower target automatically when BG and eventualBG are high
    , exercise_mode: false // when true, > 105 mg/dL high temp target adjusts sensitivityRatio for exercise_mode. This majorly changes the behavior of high temp targets from before. synonmym for high_temptarget_raises_sensitivity
    , half_basal_exercise_target: 160 // when temptarget is 160 mg/dL *and* exercise_mode=true, run 50% basal at this level (120 = 75%; 140 = 60%)
    // create maxCOB and default it to 120 because that's the most a typical body can absorb over 4 hours.
    // (If someone enters more carbs or stacks more; OpenAPS will just truncate dosing based on 120.
    // Essentially, this just limits AMA/SMB as a safety cap against excessive COB entry)
    , maxCOB: 120
    , wide_bg_target_range: false // by default use only the low end of the pump's BG target range as OpenAPS target
    // by default the higher end of the target range is used only for avoiding bolus wizard overcorrections
    // use wide_bg_target_range: true to force neutral temps over a wider range of eventualBGs
    , skip_neutral_temps: false // if true, don't set neutral temps
    , unsuspend_if_no_temp: false // if true, pump will un-suspend after a zero temp finishes
    , bolussnooze_dia_divisor: 2 // bolus snooze decays after 1/2 of DIA
    , min_5m_carbimpact: 8 // mg/dL per 5m (8 mg/dL/5m corresponds to 24g/hr at a CSF of 4 mg/dL/g (x/5*60/4))
    , autotune_isf_adjustmentFraction: 1.0 // keep autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF.  1.0 allows full adjustment, 0 is no adjustment from pump ISF.
    , remainingCarbsFraction: 1.0 // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption
    , remainingCarbsCap: 90 // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption
    // WARNING: use SMB with caution: it can and will automatically bolus up to max_iob worth of extra insulin
    , enableUAM: true // enable detection of unannounced meal carb absorption
    , A52_risk_enable: false
    , enableSMB_with_COB: false // enable supermicrobolus while COB is positive
    , enableSMB_with_temptarget: false // enable supermicrobolus for eating soon temp targets
    // *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
    // xDrip+, LimiTTer, etc. do not properly filter out high-noise SGVs
    // Using SMB overnight with such data sources risks causing a dangerous overdose of insulin
    // if the CGM sensor reads falsely high and doesn't come down as actual BG does
    , enableSMB_always: false // always enable supermicrobolus (unless disabled by high temptarget)
    , enableSMB_after_carbs: false // enable supermicrobolus for 6h after carbs, even with 0 COB
    // *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
    , allowSMB_with_high_temptarget: false // allow supermicrobolus (if otherwise enabled) even with high temp targets
    , maxSMBBasalMinutes: 30 // maximum minutes of basal that can be delivered as a single SMB with uncovered COB
    , curve: "rapid-acting" // change this to "ultra-rapid" for Fiasp, or "bilinear" for old curve
    , useCustomPeakTime: false // allows changing insulinPeakTime
    , insulinPeakTime: 75 // number of minutes after a bolus activity peaks.  defaults to 55m for Fiasp if useCustomPeakTime: false
    , carbsReqThreshold: 1 // grams of carbsReq to trigger a pushover
    , offline_hotspot: false // enabled an offline-only local wifi hotspot if no Internet available
    , noisyCGMTargetMultiplier: 1.3 // increase target by this amount when looping off raw/noisy CGM data
    , suspend_zeros_iob: false // recognize pump suspends as non insulin delivery events
    // TODO: make maxRaw a preference here usable by oref0-raw in myopenaps-cgm-loop
    //, maxRaw: 200 // highest raw/noisy CGM value considered safe to use for looping
  };
  return profile;
}

function displayedDefaults () {
    var allDefaults = defaults();
    var profile = { };

    profile.max_iob = allDefaults.max_iob;
    profile.max_daily_safety_multiplier = allDefaults.max_daily_safety_multiplier;
    profile.current_basal_safety_multiplier= allDefaults.current_basal_safety_multiplier;
    profile.autosens_max = allDefaults.autosens_max;
    profile.autosens_min = allDefaults.autosens_min;
    profile.rewind_resets_autosens = allDefaults.rewind_resets_autosens;
    profile.adv_target_adjustments = allDefaults.adv_target_adjustments;
    profile.exercise_mode = allDefaults.exercise_mode;
    profile.wide_bg_target_range = allDefaults.wide_bg_target_range;
    profile.sensitivity_raises_target = allDefaults.sensitivity_raises_target;
    profile.unsuspend_if_no_temp = allDefaults.unsuspend_if_no_temp;
    profile.enableSMB_with_COB = allDefaults.enableSMB_with_COB;
    profile.enableSMB_with_temptarget = allDefaults.enableSMB_with_temptarget;
    profile.enableUAM = allDefaults.enableUAM;
    profile.curve = allDefaults.curve;
    profile.offline_hotspot = allDefaults.offline_hotspot;

    console.error(profile);
    return profile
}

function generate (inputs, opts) {
  var profile = opts && opts.type ? opts : defaults( );

  // check if inputs has overrides for any of the default prefs
  // and apply if applicable
  for (var pref in profile) {
    if (inputs.hasOwnProperty(pref)) {
      profile[pref] = inputs[pref];
    }
  }

  var pumpsettings_data = inputs.settings;
  if (inputs.settings.insulin_action_curve > 1) {
    profile.dia =  pumpsettings_data.insulin_action_curve;
  } else {
    console.error('DIA of', profile.dia, 'is not supported');
    return -1;
  }

  if (inputs.model) {
    profile.model = inputs.model;
  }
  profile.skip_neutral_temps = inputs.skip_neutral_temps;

  profile.current_basal = basal.basalLookup(inputs.basals);
  profile.basalprofile = inputs.basals;

  _.forEach(profile.basalprofile, function(basalentry) {
    basalentry.rate = +(Math.round(basalentry.rate + "e+3")  + "e-3");
  });

  profile.max_daily_basal = basal.maxDailyBasal(inputs);
  profile.max_basal = basal.maxBasalLookup(inputs);
  if (profile.current_basal === 0) {
    console.error("current_basal of",profile.current_basal,"is not supported");
    return -1;
  }
  if (profile.max_daily_basal === 0) {
    console.error("max_daily_basal of",profile.max_daily_basal,"is not supported");
    return -1;
  }
  if (profile.max_basal < 0.1) {
    console.error("max_basal of",profile.max_basal,"is not supported");
    return -1;
  }

  var range = targets.bgTargetsLookup(inputs, profile);
  profile.out_units = inputs.targets.user_preferred_units;
  profile.min_bg = Math.round(range.min_bg);
  profile.max_bg = Math.round(range.max_bg);
  profile.bg_targets = inputs.targets;

  _.forEach(profile.bg_targets.targets, function(bg_entry) {
    bg_entry.high = Math.round(bg_entry.high);
    bg_entry.low = Math.round(bg_entry.low);
    bg_entry.min_bg = Math.round(bg_entry.min_bg);
    bg_entry.max_bg = Math.round(bg_entry.max_bg);
  });

  delete profile.bg_targets.raw;

  profile.temptargetSet = range.temptargetSet;
  profile.sens = isf.isfLookup(inputs.isf);
  profile.isfProfile = inputs.isf;
  if (profile.sens < 5) {
    console.error("ISF of",profile.sens,"is not supported");
    return -1;
  }
  if (typeof(inputs.carbratio) != "undefined") {
    profile.carb_ratio = carb_ratios.carbRatioLookup(inputs, profile);
    profile.carb_ratios = inputs.carbratio;
  } else {
    console.error("Profile wasn't given carb ratio data, cannot calculate carb_ratio");
  }
  return profile;
}

generate.defaults = defaults;
generate.displayedDefaults = displayedDefaults;
exports = module.exports = generate;

<|endoftext|>


<commit_before>const EventEmitter = require('events')
const table = require('markdown-table')

// eslint-disable-next-line import/no-unresolved
const ParserEnum = require('@vuedoc/parser/lib/Enum')
const { StringUtils } = require('@vuedoc/parser/lib/StringUtils')

// eslint-disable-next-line import/no-unresolved
const { Parser } = require('@vuedoc/parser/lib/parser/Parser')

const tag = (level) => {
  if (level > 6) {
    level = 6
  }

  return '#'.repeat(level)
}

const DEFAULT_LEVEL = 1
const DEFAULT_TABLE = true
const DEFAULT_TITLES = {
  props: 'props',
  data: 'data',
  computed: 'computed properties',
  methods: 'methods',
  events: 'events',
  slots: 'slots'
}

const DEFAULT_INDENT_LEVEL = 1
const DEFAULT_INDENT_SLUG = '  '

/* eslint-disable-next-line arrow-body-style */
const indent = (level = DEFAULT_INDENT_LEVEL, slug = DEFAULT_INDENT_SLUG) => {
  return slug.repeat(level)
}

const bold = (text) => `**${text}**`
const italic = (text) => `*${text}*`
const backtick = (text) => '`' + `${text}`.replace(/`/g, '\\`') + '`'
const item = (text) => `- ${text}`

// eslint-disable-next-line prefer-template
const h = (text, level) => tag(level) + ' ' + text
const parenthesis = (text) => `(${text})`

const Fields = {
  keywords: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'description'
    }
  ],
  props: [
    {
      key: 'name',
      highlight: true,
      required: (item) => item.required
    },
    {
      key: 'type',
      highlight: true
    },
    {
      key: 'default',
      highlight: true,
      formated: true
    },
    {
      key: 'description'
    }
  ],
  data: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'type',
      highlight: true
    },
    {
      key: 'initial',
      highlight: true,
      formated: true
    },
    {
      key: 'description'
    }
  ],
  computed: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'description'
    },
    {
      key: 'dependencies',
      highlight: true
    }
  ],
  methods: [
    {
      key: 'name',
      highlight: true,
      render: (value, item) => {
        const params = item.params.map(({ name, type, declaration, description, defaultValue }) => {
          return declaration
            ? declaration.value
            : `${name}: ${type || 'any'}`
        })

        const args = params.join(', ')

        return `${value}(${args}): ${item.return.type}`
      }
    },
    {
      key: 'description'
    }
  ],
  slots: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'description'
    },
    {
      key: 'props'
    }
  ],
  events: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'description'
    },
    {
      key: 'arguments',
      separator: '
',
      render: ({ name, type, declaration, definition }) => {
        const tokens = [
          bold(backtick(name)),
          italic(`(${type || 'any'})`)
        ]

        return item(tokens.join(' '))
      }
    }
  ]
}

function parseEntryValue (type, value) {
  if (value === ParserEnum.UNDEFINED) {
    return undefined;
  }

  switch (type) {
    case 'string':
      if (value === '\'\'') {
        return value
      }

      value = value.replace(/'/g, '\'')

      return `'${value}'`

    default:
      return StringUtils.parse(value)
  }
}

function parseType (type) {
  return type instanceof Array ? type.join('|') : type
}

function indentText (
  text,
  level = DEFAULT_INDENT_LEVEL,
  slug = DEFAULT_INDENT_SLUG
) {
  return text.split(/\n/g)
    .map((line) => indent(level, slug) + line)
    .join('\n')
}

function parseValue (value, item, { highlight, required, formated, render }) {
  if (value === ParserEnum.UNDEFINED) {
    return ''
  }

  let data = typeof render === 'function'
    ? render(value, item).replace(/\n/g, '
')
    : value

  if (typeof value === 'string') {
    data = data.replace(/\n\n/g, '
').replace(/\n/g, ' ')

    if (highlight && data) {
      data = backtick(data)
    }

    if (formated && value === "''") {
      data += ' ' + italic('empty string')
    }
  } else if (highlight) {
    data = backtick(data)
  }

  if (typeof required === 'function' && required(item)) {
    data = `${data} ${italic('required')}`
  }

  return data
}

function printTable (items, options) {
  const header = options.fields.map((item) => item.key)
  const data = items.map((item) => {
    return options.fields.reduce((accumulator, { key, separator = ', ', ...fieldOptions }) => {
      const value = item[key] instanceof Array
        ? item[key].map((val) => parseValue(val, item, fieldOptions)).join(separator)
        : parseValue(item[key], item, fieldOptions)

      accumulator.push(value)

      return accumulator
    }, [])
  })

  options.$println(table([header, ...data]))
  options.$println()
}

function printParams (params, options, title = 'parameters') {
  if (!params.length) {
    return
  }

  options.$println(indentText(bold(`${title}:`)))
  options.$println()

  const level = 2

  params.forEach((param) => {
    const tokens = [
      indent(level) + item(backtick(param.name))
    ]

    if (param.type) {
      const type = param.type instanceof Array
        ? param.type.join('|')
        : param.type

      tokens.push(bold(type))
    }

    if (param.optional) {
      tokens.push(italic('(optional)'))
    }

    if (param.default) {
      tokens.push(backtick(`default: ${param.default}`))
    }

    if (param.description) {
      tokens.push('-')
      tokens.push(param.description)
    }

    options.$println.apply(null, tokens)
  })

  options.$println()
}

const writer = {
  name (options, name) {
    options.$println(h(name, options.level++))
    options.$println()
  },

  description (options, description) {
    options.$println(description)
    options.$println()
  },

  keywords (options, keywords) {
    if (options.table) {
      printTable(keywords, { ...options, fields: Fields.keywords })
    } else {
      keywords.forEach(({ name, description }) => {
        let line = bold(name)

        if (description) {
          line += ` - ${indentText(description).trim()}`
        }

        options.$println(item(line))
      })
    }

    options.$println()
  },

  props (options, props, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(props, { ...options, fields: Fields.props })
    } else {
      props.forEach(({ name, description, keywords, ...prop }) => {
        const customTypeKeyword = keywords.find(({ name }) => name === 'type')

        const customType = customTypeKeyword
          && customTypeKeyword.description.replace(/\{(.+?)\}/, '$1')
        const type = customType || prop.type || prop.nativeType || 'Any'
        const nature = prop.required ? 'required' : 'optional'

        const defaultKeyword = keywords.find(({ name }) => name === 'default')

        const defaultValue = defaultKeyword
          ? defaultKeyword.description
          : parseEntryValue(prop.nativeType, prop.default)

        const tokens = [
          item(backtick(name)),
          bold(italic(parseType(type))),
          parenthesis(italic(nature))
        ]

        if (defaultValue && defaultValue !== ParserEnum.UNDEFINED) {
          tokens.push(backtick(`default: ${defaultValue}`))
        }

        options.$print.apply(null, tokens)

        options.$println()
        options.$println()

        if (description) {
          options.$println(indentText(description))
          options.$println()
        }
      })
    }
  },

  data (options, data, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(data, { ...options, fields: Fields.data })
    } else {
      data.forEach(({ name, description, type, initial }) => {
        options.$println(item(backtick(name)))
        options.$println()

        if (description) {
          options.$println(indentText(description))
          options.$println()
        }

        const value = backtick(parseEntryValue(type, initial))

        options.$print(bold('initial value:'), value)
        options.$println()
        options.$println()
      })
    }
  },

  computed (options, props, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(props, { ...options, fields: Fields.computed })
    } else {
      props.forEach(({ name, description, dependencies }) => {
        options.$println(item(backtick(name)))

        if (description) {
          options.$println()
          options.$println(indentText(description))
        }

        if (dependencies.length) {
          const depStr = dependencies
            .map((dependency) => backtick(dependency))
            .join(', ')

          options.$println()
          options.$println(indent(), bold('dependencies:'), depStr)
          options.$println()
        }
      })
    }

    options.$println()
  },

  methods (options, methods, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(methods, { ...options, fields: Fields.methods })
    } else {
      methods.forEach(({ name, description, params, return: returns }) => {
        const args = params.map(({ name }) => name).join(', ')

        options.$println(item(backtick(`${name}(${args})`)))
        options.$println()

        if (description) {
          options.$println(indentText(description))
          options.$println()
        }

        printParams(params, options)

        if (returns.type === 'void' && !returns.description) {
          return
        }

        options.$println(indentText(bold('return value:')))
        options.$println()

        options.$println(
          indentText(item(bold(returns.type)), 2), '-', returns.description
        )
      })
    }
  },

  slots (options, slots, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(slots, { ...options, fields: Fields.slots })
    } else {
      slots.forEach(({ name, description }) => {
        const tokens = [
          item(backtick(name))
        ]

        if (description) {
          tokens.push(indentText(description).trim())
        }

        options.$print.apply(null, tokens)
        options.$println()
        options.$println()
      })
    }
  },

  events (options, events, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(events, { ...options, fields: Fields.events })
    } else {
      events.forEach(({ name, description, arguments: args }) => {
        options.$println(item(backtick(name)))
        options.$println()

        if (description) {
          options.$println(indentText(description))
          options.$println()
        }

        printParams(args, options, 'arguments')
      })
    }
  }
}

module.exports.render = (component, options = {}) => {
  const emiter = new EventEmitter()

  options.level = options.level || DEFAULT_LEVEL
  options.table = options.hasOwnProperty('table') ? options.table : DEFAULT_TABLE
  options.titles = options.titles || DEFAULT_TITLES
  options.features = options.features || Parser.SUPPORTED_FEATURES

  options.$print = (...args) => emiter.emit('write', args.join(' '))

  options.$println = (...args) => {
    options.$print.apply(null, args)
    emiter.emit('write', '\n')
  }

  process.nextTick(() => {
    options.features.forEach((node) => {
      if (!component[node] || component[node].length === 0) {
        return
      }

      const title = options.titles[node] || DEFAULT_TITLES[node]

      writer[node](options, component[node], title)
    })

    emiter.emit('end')
  })

  return emiter
}
<commit_msg>Improve rendering of methods<commit_after>const EventEmitter = require('events')
const table = require('markdown-table')

// eslint-disable-next-line import/no-unresolved
const ParserEnum = require('@vuedoc/parser/lib/Enum')
const { StringUtils } = require('@vuedoc/parser/lib/StringUtils')
const { Value } = require('@vuedoc/parser/lib/entity/Value')

// eslint-disable-next-line import/no-unresolved
const { Parser } = require('@vuedoc/parser/lib/parser/Parser')

const tag = (level) => {
  if (level > 6) {
    level = 6
  }

  return '#'.repeat(level)
}

const DEFAULT_LEVEL = 1
const DEFAULT_TABLE = true
const DEFAULT_TITLES = {
  props: 'props',
  data: 'data',
  computed: 'computed properties',
  methods: 'methods',
  events: 'events',
  slots: 'slots'
}

const DEFAULT_INDENT_LEVEL = 1
const DEFAULT_INDENT_SLUG = '  '

/* eslint-disable-next-line arrow-body-style */
const indent = (level = DEFAULT_INDENT_LEVEL, slug = DEFAULT_INDENT_SLUG) => {
  return slug.repeat(level)
}

const bold = (text) => `**${text}**`
const italic = (text) => `*${text}*`
const backtick = (text) => '`' + `${text}`.replace(/`/g, '\\`') + '`'
const code = (text, lang) => '```' + lang + '\n' + text + '\n' + '```'
const item = (text) => `- ${text}`

// eslint-disable-next-line prefer-template
const h = (text, level) => tag(level) + ' ' + text
const parenthesis = (text) => `(${text})`

function renderMethodTitle (value, method) {
  const params = method.params
    .filter(({ name }) => name.indexOf('.') === -1)
    .map(({ name, type, declaration, defaultValue, optional, default: defaultParamValue }) => {
      let paramType = type instanceof Array ? type.join(' | ') : type
      let paramDefaultValue = defaultParamValue ? JSON.stringify(defaultParamValue) : null
      let paramName = declaration ? declaration.value : name

      if (optional) {
        paramName += '?'
      }

      if (defaultValue instanceof Value) {
        paramDefaultValue = defaultValue.type === ParserEnum.Syntax.ObjectExpression
          ? JSON.stringify(defaultValue.value)
          : defaultValue.value
      }

      if (paramType) {
        if (paramType === 'Any') {
          paramType = 'any'
        }
      } else {
        paramType = paramDefaultValue ? typeof paramDefaultValue : 'any'
      }

      let arg = `${paramName}: ${paramType}`

      if (paramDefaultValue) {
        arg += ` = ${paramDefaultValue}`
      }

      return arg
    })

  const args = params.join(', ')

  return `${value}(${args}): ${method.return.type}`
}

const Fields = {
  keywords: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'description'
    }
  ],
  props: [
    {
      key: 'name',
      highlight: true,
      required: (item) => item.required
    },
    {
      key: 'type',
      highlight: true
    },
    {
      key: 'default',
      highlight: true,
      formated: true
    },
    {
      key: 'description'
    }
  ],
  data: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'type',
      highlight: true
    },
    {
      key: 'initial',
      highlight: true,
      formated: true
    },
    {
      key: 'description'
    }
  ],
  computed: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'description'
    },
    {
      key: 'dependencies',
      highlight: true
    }
  ],
  methods: [
    {
      key: 'name',
      highlight: true,
      render: renderMethodTitle
    },
    {
      key: 'description'
    }
  ],
  slots: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'description'
    },
    {
      key: 'props'
    }
  ],
  events: [
    {
      key: 'name',
      highlight: true
    },
    {
      key: 'description'
    },
    {
      key: 'arguments',
      separator: '
',
      render: ({ name, type, declaration, definition }) => {
        const tokens = [
          bold(backtick(name)),
          italic(`(${type || 'any'})`)
        ]

        return item(tokens.join(' '))
      }
    }
  ]
}

function parseEntryValue (type, value) {
  if (value === ParserEnum.UNDEFINED) {
    return undefined;
  }

  switch (type) {
    case 'string':
      if (value === '\'\'') {
        return value
      }

      value = value.replace(/'/g, '\'')

      return `'${value}'`

    default:
      return StringUtils.parse(value)
  }
}

function parseType (type) {
  return type instanceof Array ? type.join('|') : type
}

function indentText (
  text,
  level = DEFAULT_INDENT_LEVEL,
  slug = DEFAULT_INDENT_SLUG
) {
  return text.split(/\n/g)
    .map((line) => indent(level, slug) + line)
    .join('\n')
}

function parseValue (value, item, { highlight, required, formated, render }) {
  if (value === ParserEnum.UNDEFINED) {
    return ''
  }

  let data = typeof render === 'function'
    ? render(value, item).replace(/\n/g, '
')
    : value

  if (typeof value === 'string') {
    data = data.replace(/\n\n/g, '
').replace(/\n/g, ' ')

    if (highlight && data) {
      data = backtick(data)
    }

    if (formated && value === "''") {
      data += ' ' + italic('empty string')
    }
  } else if (highlight) {
    data = backtick(data)
  }

  if (typeof required === 'function' && required(item)) {
    data = `${data} ${italic('required')}`
  }

  return data
}

function printTable (items, options) {
  const header = options.fields.map((item) => item.key)
  const data = items.map((item) => {
    return options.fields.reduce((accumulator, { key, separator = ', ', ...fieldOptions }) => {
      const value = item[key] instanceof Array
        ? item[key].map((val) => parseValue(val, item, fieldOptions)).join(separator)
        : parseValue(item[key], item, fieldOptions)

      accumulator.push(value)

      return accumulator
    }, [])
  })

  options.$println(table([header, ...data]))
  options.$println()
}

function printParams (params, options, title = 'Parameters') {
  if (!params.length) {
    return
  }

  options.$println(indentText(bold(title)))
  options.$println()

  const level = 2

  params.forEach((param) => {
    const tokens = [
      indent(level) + item(backtick(param.name))
    ]

    if (param.type) {
      const type = param.type instanceof Array
        ? param.type.join('|')
        : param.type

      tokens.push(bold(type))
    }

    if (param.optional) {
      tokens.push(italic('(optional)'))
    }

    if (param.default) {
      tokens.push(backtick(`default: ${param.default}`))
    }

    if (param.description) {
      tokens.push('-')
      tokens.push(param.description)
    }

    options.$println.apply(null, tokens)
  })

  options.$println()
}

const writer = {
  name (options, name) {
    options.$println(h(name, options.level++))
    options.$println()
  },

  description (options, description) {
    options.$println(description)
    options.$println()
  },

  keywords (options, keywords) {
    if (options.table) {
      printTable(keywords, { ...options, fields: Fields.keywords })
    } else {
      keywords.forEach(({ name, description }) => {
        let line = bold(name)

        if (description) {
          line += ` - ${indentText(description).trim()}`
        }

        options.$println(item(line))
      })
    }

    options.$println()
  },

  props (options, props, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(props, { ...options, fields: Fields.props })
    } else {
      props.forEach(({ name, description, keywords, ...prop }) => {
        const customTypeKeyword = keywords.find(({ name }) => name === 'type')

        const customType = customTypeKeyword
          && customTypeKeyword.description.replace(/\{(.+?)\}/, '$1')
        const type = customType || prop.type || prop.nativeType || 'Any'
        const nature = prop.required ? 'required' : 'optional'

        const defaultKeyword = keywords.find(({ name }) => name === 'default')

        const defaultValue = defaultKeyword
          ? defaultKeyword.description
          : parseEntryValue(prop.nativeType, prop.default)

        const tokens = [
          item(backtick(name)),
          bold(italic(parseType(type))),
          parenthesis(italic(nature))
        ]

        if (defaultValue && defaultValue !== ParserEnum.UNDEFINED) {
          tokens.push(backtick(`default: ${defaultValue}`))
        }

        options.$print.apply(null, tokens)

        options.$println()
        options.$println()

        if (description) {
          options.$println(indentText(description))
          options.$println()
        }
      })
    }
  },

  data (options, data, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(data, { ...options, fields: Fields.data })
    } else {
      data.forEach(({ name, description, type, initial }) => {
        options.$println(item(backtick(name)))
        options.$println()

        if (description) {
          options.$println(indentText(description))
          options.$println()
        }

        const value = backtick(parseEntryValue(type, initial))

        options.$print(bold('initial value:'), value)
        options.$println()
        options.$println()
      })
    }
  },

  computed (options, props, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(props, { ...options, fields: Fields.computed })
    } else {
      props.forEach(({ name, description, dependencies }) => {
        options.$println(item(backtick(name)))

        if (description) {
          options.$println()
          options.$println(indentText(description))
        }

        if (dependencies.length) {
          const depStr = dependencies
            .map((dependency) => backtick(dependency))
            .join(', ')

          options.$println()
          options.$println(indent(), bold('dependencies:'), depStr)
          options.$println()
        }
      })
    }

    options.$println()
  },

  methods (options, methods, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (!options.table) {
      // printTable(methods, { ...options, fields: Fields.methods })
    } else {
      methods.forEach((method) => {
        options.$println(h(`${method.name}()`, options.level + 1))
        options.$println()

        if (method.description) {
          options.$println(indentText(method.description))
          options.$println()
        }

        options.$println(bold('Syntax'))
        options.$println()

        const syntaxKeyword = method.keywords.find(({ name }) => name === 'syntax')

        if (syntaxKeyword) {
          options.$println(code(syntaxKeyword.description, 'ts'))
        } else {
          options.$println(code(renderMethodTitle(method.name, method), 'ts'))
        }

        options.$println()

        if (method.params.length) {
          options.$println(bold('Parameters'))
          options.$println()

          method.params.forEach(({ name, description }) => {
            options.$print(item(bold(backtick(name))))
            options.$println('
')
            options.$println(description)
          })

          options.$println()
        }

        if (method.return.description) {
          options.$println(bold('Return value'))
          options.$println()
          options.$println(method.return.description)
        }
      })
    }
  },

  slots (options, slots, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(slots, { ...options, fields: Fields.slots })
    } else {
      slots.forEach(({ name, description }) => {
        const tokens = [
          item(backtick(name))
        ]

        if (description) {
          tokens.push(indentText(description).trim())
        }

        options.$print.apply(null, tokens)
        options.$println()
        options.$println()
      })
    }
  },

  events (options, events, title) {
    options.$println(h(title, options.level))
    options.$println()

    if (options.table) {
      printTable(events, { ...options, fields: Fields.events })
    } else {
      events.forEach(({ name, description, arguments: args }) => {
        options.$println(item(backtick(name)))
        options.$println()

        if (description) {
          options.$println(indentText(description))
          options.$println()
        }

        printParams(args, options, 'arguments')
      })
    }
  }
}

module.exports.render = (component, options = {}) => {
  const emiter = new EventEmitter()

  options.level = options.level || DEFAULT_LEVEL
  options.table = options.hasOwnProperty('table') ? options.table : DEFAULT_TABLE
  options.titles = options.titles || DEFAULT_TITLES
  options.features = options.features || Parser.SUPPORTED_FEATURES

  options.$print = (...args) => emiter.emit('write', args.join(' '))

  options.$println = (...args) => {
    options.$print.apply(null, args)
    emiter.emit('write', '\n')
  }

  process.nextTick(() => {
    options.features.forEach((node) => {
      if (!component[node] || component[node].length === 0) {
        return
      }

      const title = options.titles[node] || DEFAULT_TITLES[node]

      writer[node](options, component[node], title)
    })

    emiter.emit('end')
  })

  return emiter
}
<|endoftext|>


<commit_before>// (c) 2013 Manuel Braun (mb@w69b.com)
goog.provide('w69b.LocalVideoCapturer');
goog.require('goog.Disposable');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.math.Size');
goog.require('w69b.imgtools');

goog.scope(function() {
  var Size = goog.math.Size;
  var imgtools = w69b.imgtools;
  /**
   * TODO: add start/stop methods and ready/error events.
   * @constructor
   * @extends {goog.Disposable}
   */
  w69b.LocalVideoCapturer = function() {
    goog.base(this);
    this.backCanvas_ = /** @type {HTMLCanvasElement} */ (
      document.createElement('canvas'));
    this.mediaVideo_ = /** @type {HTMLVideoElement} */ (
      document.createElement('video'));
    this.mediaVideo_.setAttribute('autoplay', 'true');
    this.backContext_ = /** @type {CanvasRenderingContext2D} */ (
      this.backCanvas_.getContext('2d'));
  };
  var LocalVideoCapturer = w69b.LocalVideoCapturer;
  goog.inherits(LocalVideoCapturer, goog.Disposable);
  var pro = LocalVideoCapturer.prototype;

  /**
   * Alias to getUserMedia functions.
   * @type {Function}
   */
  LocalVideoCapturer.getMedia = (
    navigator['getUserMedia'] ||
      navigator['webkitGetUserMedia'] ||
      navigator['mozGetUserMedia'] ||
      navigator['msGetUserMedia']);

  if (LocalVideoCapturer.getMedia)
    LocalVideoCapturer.getMedia =
      LocalVideoCapturer.getMedia.bind(navigator);

  /**
   * Canvas uses to call getImageData on.
   * @type {HTMLCanvasElement}
   * @private
   */
  pro.backCanvas_ = null;
  /**
   * Rendering context of back canvas.
   * @type {CanvasRenderingContext2D}
   * @private
   */
  pro.backContext_ = null;
  /**
   * Video element used to render the getUserMedia stream.
   * @type {HTMLVideoElement}
   * @private
   */
  pro.mediaVideo_ = null;

  pro.stream_ = null;

  /**
   * @return {HTMLVideoElement} video element.
   */
  pro.getVideo = function() {
    return this.mediaVideo_;
  };

  /**
   * Start capturing video.
   */
  pro.start = function(ready) {
    goog.events.listenOnce(this.mediaVideo_, 'canplay', function() {
      this.waitForVideoSize_(ready);
    }, false, this);
    this.getUserMedia();
  };

  /**
   * Calls ready when videoSize gets greater than 0.
   * Sometimes the video size is 0 in FireFox even after canplay has been
   * triggered. This works arround this by polling the video with.
   * @private
   */
  pro.waitForVideoSize_ = function(ready) {
    if (this.mediaVideo_.videoWidth > 0 && this.mediaVideo_.videoHeight > 0) {
      ready();
    } else {
      window.setTimeout(this.waitForVideoSize_.bind(this, ready), 100);
    }
  };

  /**
   * Get Image data of current frame from local video stream.
   * Image is scaled down to opt_maxSize if its width or height is larger.
   * @param {Size} size desired size of image.
   * @return {ImageData} image data.
   */
  pro.getImageData = function(size) {
    this.drawAndGetCanvas(size);
    return this.backContext_.getImageData(0, 0, size.width, size.height);
  };

  /**
   * Get canvas with current frame from local video stream.
   * Image is scaled down to opt_maxSize if its width or height is larger.
   * @param {Size} size desired size of image.
   * @return {HTMLCanvasElement} canvas.
   */
  pro.drawAndGetCanvas = function(size) {
    var video = this.mediaVideo_;
    var canvas = this.backCanvas_;
    goog.asserts.assert(video.videoWidth > 0 && video.videoWidth > 0);

    // Rescale canvas if needed.
    if (canvas.width != size.width || canvas.height != size.height) {
      canvas.width = size.width;
      canvas.height = size.height;
    }
    var context = this.backContext_;
    this.drawOnCanvas(canvas, context);
    return canvas;
  };

  /**
   * Draws video on canvas, scaling to to fit into canvas.
   * @param {HTMLCanvasElement} canvas canvas to draw on.
   * @param {CanvasRenderingContext2D} context context of canvas.
   */
  pro.drawOnCanvas = function(canvas, context) {
    var video = this.getVideo();
    var width = canvas.width;
    var height = canvas.height;

    // Smallest scale that scales video to desired size.
    var scale = Math.max(height / video.videoHeight, width / video.videoWidth);
    // draw image cropping what does not fit on the right/bottom edges.
    context.drawImage(video, 0, 0,
      video.videoWidth * scale, video.videoHeight * scale);

  };

  /**
   * video stream.
   * @protected
   */
  pro.onGetMediaSuccess = function(stream) {
    // If disposed since, dont do anything.
    if (this.mediaVideo_ === null)
      return;
    this.mediaVideo_.src = window.URL.createObjectURL(stream);
    this.mediaVideo_.play();
    this.stream_ = stream;
  };

  /**
   * code error code.
   */
  pro.onGetMediaError = function(code) {
    window.console.log('error code:');
    window.console.log(code);
  };

  /**
   * Starts get user media.
   * @protected
   */
  pro.getUserMedia = function() {
    var self = this;
    function getMedia(deviceId) {
      var constraint = {'optional': [{'facingMode': 'environment'}]};
      if (deviceId)
        constraint = {'optional': [{'sourceId': deviceId}]};
      LocalVideoCapturer.getMedia({'video': constraint},
        self.onGetMediaSuccess.bind(self),
        self.onGetMediaError.bind(self));
    }
    function gotSources(sources) {
      for (var i = 0; i < sources.length; ++i) {
        var source = sources[i];
        if (source['kind'] === 'video' && source['facing'] == 'environment') {
          getMedia(source.id);
          break;
        }
      }
    }
    if (navigator['mediaDevices'] && navigator['mediaDevices']['enumerateDevices']) {
      // Hack while facingMode not supported, just use last device.
      navigator['mediaDevices']['enumerateDevices']().then(function(devs) {
        devs = devs.filter(function(dev) { return dev['kind'] === 'videoinput'; });
        if (devs.length)
          getMedia(devs.slice(-1)[0]['deviceId']);
        else
          getMedia([]);
      });
    } else if (window['MediaStreamTrack'] && window['MediaStreamTrack']['getSources']) {
      window['MediaStreamTrack']['getSources'](gotSources);
    } else {
      gotSources([]);
    }
  };

  /**
   * @override
   */
  pro.disposeInternal = function() {
    var url = this.mediaVideo_.src;
    this.mediaVideo_.pause();
    this.mediaVideo_.src = '';
    if (window.URL && window.URL.revokeObjectURL)
      window.URL.revokeObjectURL(url);
    this.mediaVideo_ = null;
    if (this.stream_) {
      if (this.stream_.stop)
        this.stream_.stop();
      if (this.stream_['getTracks']) {
        this.stream_['getTracks']().forEach(function(track) {
          track.stop();
        });
      }
    }
  };
});
<commit_msg>fix: replace deprecated URL.createObjectURL with srcObject<commit_after>// (c) 2013 Manuel Braun (mb@w69b.com)
goog.provide('w69b.LocalVideoCapturer');
goog.require('goog.Disposable');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.math.Size');
goog.require('w69b.imgtools');

goog.scope(function() {
  var Size = goog.math.Size;
  var imgtools = w69b.imgtools;
  /**
   * TODO: add start/stop methods and ready/error events.
   * @constructor
   * @extends {goog.Disposable}
   */
  w69b.LocalVideoCapturer = function() {
    goog.base(this);
    this.backCanvas_ = /** @type {HTMLCanvasElement} */ (
      document.createElement('canvas'));
    this.mediaVideo_ = /** @type {HTMLVideoElement} */ (
      document.createElement('video'));
    this.mediaVideo_.setAttribute('autoplay', 'true');
    this.backContext_ = /** @type {CanvasRenderingContext2D} */ (
      this.backCanvas_.getContext('2d'));
  };
  var LocalVideoCapturer = w69b.LocalVideoCapturer;
  goog.inherits(LocalVideoCapturer, goog.Disposable);
  var pro = LocalVideoCapturer.prototype;

  /**
   * Alias to getUserMedia functions.
   * @type {Function}
   */
  LocalVideoCapturer.getMedia = (
    navigator['getUserMedia'] ||
      navigator['webkitGetUserMedia'] ||
      navigator['mozGetUserMedia'] ||
      navigator['msGetUserMedia']);

  if (LocalVideoCapturer.getMedia)
    LocalVideoCapturer.getMedia =
      LocalVideoCapturer.getMedia.bind(navigator);

  /**
   * Canvas uses to call getImageData on.
   * @type {HTMLCanvasElement}
   * @private
   */
  pro.backCanvas_ = null;
  /**
   * Rendering context of back canvas.
   * @type {CanvasRenderingContext2D}
   * @private
   */
  pro.backContext_ = null;
  /**
   * Video element used to render the getUserMedia stream.
   * @type {HTMLVideoElement}
   * @private
   */
  pro.mediaVideo_ = null;

  pro.stream_ = null;

  /**
   * @return {HTMLVideoElement} video element.
   */
  pro.getVideo = function() {
    return this.mediaVideo_;
  };

  /**
   * Start capturing video.
   */
  pro.start = function(ready) {
    goog.events.listenOnce(this.mediaVideo_, 'canplay', function() {
      this.waitForVideoSize_(ready);
    }, false, this);
    this.getUserMedia();
  };

  /**
   * Calls ready when videoSize gets greater than 0.
   * Sometimes the video size is 0 in FireFox even after canplay has been
   * triggered. This works arround this by polling the video with.
   * @private
   */
  pro.waitForVideoSize_ = function(ready) {
    if (this.mediaVideo_.videoWidth > 0 && this.mediaVideo_.videoHeight > 0) {
      ready();
    } else {
      window.setTimeout(this.waitForVideoSize_.bind(this, ready), 100);
    }
  };

  /**
   * Get Image data of current frame from local video stream.
   * Image is scaled down to opt_maxSize if its width or height is larger.
   * @param {Size} size desired size of image.
   * @return {ImageData} image data.
   */
  pro.getImageData = function(size) {
    this.drawAndGetCanvas(size);
    return this.backContext_.getImageData(0, 0, size.width, size.height);
  };

  /**
   * Get canvas with current frame from local video stream.
   * Image is scaled down to opt_maxSize if its width or height is larger.
   * @param {Size} size desired size of image.
   * @return {HTMLCanvasElement} canvas.
   */
  pro.drawAndGetCanvas = function(size) {
    var video = this.mediaVideo_;
    var canvas = this.backCanvas_;
    goog.asserts.assert(video.videoWidth > 0 && video.videoWidth > 0);

    // Rescale canvas if needed.
    if (canvas.width != size.width || canvas.height != size.height) {
      canvas.width = size.width;
      canvas.height = size.height;
    }
    var context = this.backContext_;
    this.drawOnCanvas(canvas, context);
    return canvas;
  };

  /**
   * Draws video on canvas, scaling to to fit into canvas.
   * @param {HTMLCanvasElement} canvas canvas to draw on.
   * @param {CanvasRenderingContext2D} context context of canvas.
   */
  pro.drawOnCanvas = function(canvas, context) {
    var video = this.getVideo();
    var width = canvas.width;
    var height = canvas.height;

    // Smallest scale that scales video to desired size.
    var scale = Math.max(height / video.videoHeight, width / video.videoWidth);
    // draw image cropping what does not fit on the right/bottom edges.
    context.drawImage(video, 0, 0,
      video.videoWidth * scale, video.videoHeight * scale);

  };

  /**
   * video stream.
   * @protected
   */
  pro.onGetMediaSuccess = function(stream) {
    // If disposed since, dont do anything.
    if (this.mediaVideo_ === null)
      return;
    this.mediaVideo_.srcObject = stream;
    this.mediaVideo_.play();
    this.stream_ = stream;
  };

  /**
   * code error code.
   */
  pro.onGetMediaError = function(code) {
    window.console.log('error code:');
    window.console.log(code);
  };

  /**
   * Starts get user media.
   * @protected
   */
  pro.getUserMedia = function() {
    var self = this;
    function getMedia(deviceId) {
      var constraint = {'optional': [{'facingMode': 'environment'}]};
      if (deviceId)
        constraint = {'optional': [{'sourceId': deviceId}]};
      LocalVideoCapturer.getMedia({'video': constraint},
        self.onGetMediaSuccess.bind(self),
        self.onGetMediaError.bind(self));
    }
    function gotSources(sources) {
      for (var i = 0; i < sources.length; ++i) {
        var source = sources[i];
        if (source['kind'] === 'video' && source['facing'] == 'environment') {
          getMedia(source.id);
          break;
        }
      }
    }
    if (navigator['mediaDevices'] && navigator['mediaDevices']['enumerateDevices']) {
      // Hack while facingMode not supported, just use last device.
      navigator['mediaDevices']['enumerateDevices']().then(function(devs) {
        devs = devs.filter(function(dev) { return dev['kind'] === 'videoinput'; });
        if (devs.length)
          getMedia(devs.slice(-1)[0]['deviceId']);
        else
          getMedia([]);
      });
    } else if (window['MediaStreamTrack'] && window['MediaStreamTrack']['getSources']) {
      window['MediaStreamTrack']['getSources'](gotSources);
    } else {
      gotSources([]);
    }
  };

  /**
   * @override
   */
  pro.disposeInternal = function() {
    this.mediaVideo_.pause();
    this.mediaVideo_.srcObject = null;
    this.mediaVideo_ = null;
    if (this.stream_) {
      if (this.stream_.stop)
        this.stream_.stop();
      if (this.stream_['getTracks']) {
        this.stream_['getTracks']().forEach(function(track) {
          track.stop();
        });
      }
    }
  };
});
<|endoftext|>


<commit_before>const fs     = require("fs")
    , moment = require("moment")

var Utils          = require("./utils")
  , Migration      = require("./migration")
  , DataTypes      = require("./data-types")

module.exports = (function() {
  var Migrator = function(sequelize, options) {
    this.sequelize = sequelize
    this.options   = Utils._.extend({
      path: __dirname + '/../migrations',
      from: null,
      to: null,
      logging: console.log,
      filesFilter: /\.js$/
    }, options || {})

    if (this.options.logging === true) {
      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
      this.options.logging = console.log
    }

    if (this.options.logging == console.log) {
      // using just console.log will break in node < 0.6
      this.options.logging = function(s) { console.log(s) }
    }
  }

  Object.defineProperty(Migrator.prototype, "queryInterface", {
    get: function() {
      return this.sequelize.getQueryInterface()
    }
  })

  Migrator.prototype.migrate = function(options) {
    var self = this

    options = Utils._.extend({
      method: 'up'
    }, options || {})

    return new Utils.CustomEventEmitter(function(emitter) {
      self.getUndoneMigrations(function(err, migrations) {
        if (err) {
          emitter.emit('error', err)
        } else {
          var chainer = new Utils.QueryChainer
            , from    = migrations[0]

          if (options.method === 'down') {
            from = migrations[0]
            migrations.reverse()
          }

          migrations.forEach(function(migration) {
            chainer.add(migration, 'execute', [options], {
              before: function(migration) {
                if (self.options.logging !== false) {
                  self.options.logging('Executing migration: ' + migration.filename)
                }
              },
              after: function(migration) {
                if (self.options.logging !== false) {
                  self.options.logging('Executed migration: ' + migration.filename)
                }
              },
              success: function(migration, callback) {
                if (options.method === 'down') {
                  deleteUndoneMigration.call(self, from, migration, callback)
                } else {
                  saveSuccessfulMigration.call(self, from, migration, callback)
                }
              }
            })
          })

          chainer
            .runSerially({ skipOnError: true })
            .success(function() { emitter.emit('success', null) })
            .error(function(err) { emitter.emit('error', err) })
        }
      })
    }).run()
  }

  Migrator.prototype.getUndoneMigrations = function(callback)  {
    var self = this

    var filterFrom = function(migrations, from, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isAfter(from, options) })
      callback && callback(null, result)
    }
    var filterTo = function(migrations, to, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isBefore(to, options) })
      callback && callback(null, result)
    }

    var migrationFiles = fs.readdirSync(this.options.path).filter(function(file) {
      return self.options.filesFilter.test(file)
    })

    var migrations = migrationFiles.map(function(file) {
      return new Migration(self, self.options.path + '/' + file)
    })

    migrations = migrations.sort(function(a,b){
      return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0])
    })

    if (this.options.from) {
      filterFrom(migrations, this.options.from, function(err, migrations) {
        if (self.options.to) {
          filterTo(migrations, self.options.to, callback)
        } else {
          callback && callback(null, migrations)
        }
      })
    } else {
      getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) {
        if (lastMigrationId) {
          filterFrom(migrations, lastMigrationId, function(err, migrations) {
            if (self.options.to) {
              filterTo(migrations, self.options.to, callback)
            } else {
              callback && callback(null, migrations)
            }
          }, { withoutEqual: true })
        } else {
          if (self.options.to) {
            filterTo(migrations, self.options.to, callback)
          } else {
            callback && callback(null, migrations)
          }
        }
      }).error(function(err) {
        callback && callback(err, null)
      })
    }
  }

  Migrator.prototype.findOrCreateSequelizeMetaDAO = function(syncOptions) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var storedDAO   = self.sequelize.daoFactoryManager.getDAO('SequelizeMeta')
        , SequelizeMeta = storedDAO

      if (!storedDAO) {
        SequelizeMeta = self.sequelize.define('SequelizeMeta', {
          from: DataTypes.STRING,
          to:   DataTypes.STRING
        }, {
          timestamps: false
        })
      }

      // force sync when model has newly created or if syncOptions are passed
      if (!storedDAO || syncOptions) {
        SequelizeMeta
          .sync(syncOptions || {})
          .success(function() { emitter.emit('success', SequelizeMeta) })
          .error(function(err) { emitter.emit('error', err) })
      } else {
        emitter.emit('success', SequelizeMeta)
      }
    }).run()
  }

  // private

  var getLastMigrationFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
        SequelizeMeta.find({ order: 'id DESC' }).success(function(meta) {
          emitter.emit('success', meta ? meta : null)
        }).error(function(err) { emitter.emit('error', err) })
      }).error(function(err) { emitter.emit(err) })
    }).run()
  }

  var getLastMigrationIdFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      getLastMigrationFromDatabase.call(self)
        .success(function(meta) {
          emitter.emit('success', meta ? meta.to : null)
        })
        .error(function(err) {
          emitter.emit('error', err)
        })
    }).run()
  }

  var getFormattedDateString = function(s) {
    var result = null

    try {
      result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
    } catch(e) {
      throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
    }

    return result
  }

  var stringToDate = function(s) {
    return moment(getFormattedDateString(s), "YYYYMMDDHHmmss")
  }

  var saveSuccessfulMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .create({ from: from.migrationId, to: to.migrationId })
        .success(callback)
    })
  }

  var deleteUndoneMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .find({ where: { from: from.migrationId.toString(), to: to.migrationId.toString() } })
        .success(function(meta) {
          meta.destroy().success(callback)
        })
    })
  }

  return Migrator
})()
<commit_msg>Make migrations more verbose and time migrations<commit_after>const fs     = require("fs")
    , moment = require("moment")

var Utils          = require("./utils")
  , Migration      = require("./migration")
  , DataTypes      = require("./data-types")

module.exports = (function() {
  var Migrator = function(sequelize, options) {
    this.sequelize = sequelize
    this.options   = Utils._.extend({
      path: __dirname + '/../migrations',
      from: null,
      to: null,
      logging: console.log,
      filesFilter: /\.js$/
    }, options || {})

    if (this.options.logging === true) {
      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
      this.options.logging = console.log
    }

    if (this.options.logging == console.log) {
      // using just console.log will break in node < 0.6
      this.options.logging = function(s) { console.log(s) }
    }
  }

  Object.defineProperty(Migrator.prototype, "queryInterface", {
    get: function() {
      return this.sequelize.getQueryInterface()
    }
  })

  Migrator.prototype.migrate = function(options) {
    var self = this

    options = Utils._.extend({
      method: 'up'
    }, options || {})

    return new Utils.CustomEventEmitter(function(emitter) {
      self.getUndoneMigrations(function(err, migrations) {
        if (err) {
          emitter.emit('error', err)
        } else {
          var chainer = new Utils.QueryChainer
            , from    = migrations[0]

          if (options.method === 'down') {
            from = migrations[0]
            migrations.reverse()
          }

          if (migrations.length === 0) {
            self.options.logging("There are no pending migrations.")
          } else {
            self.options.logging("Running migrations...")
          }
          migrations.forEach(function(migration) {
            var migrationTime
            chainer.add(migration, 'execute', [options], {
              before: function(migration) {
                if (self.options.logging !== false) {
                  self.options.logging(migration.filename)
                }
                migrationTime = process.hrtime()
              },
              after: function(migration) {
                migrationTime = process.hrtime(migrationTime)
                migrationTime = Math.round( (migrationTime[0] * 1000) + (migrationTime[1] / 1000000));

                if (self.options.logging !== false) {
                  self.options.logging('Completed in ' + migrationTime + 'ms')
                }
              },
              success: function(migration, callback) {
                if (options.method === 'down') {
                  deleteUndoneMigration.call(self, from, migration, callback)
                } else {
                  saveSuccessfulMigration.call(self, from, migration, callback)
                }
              }
            })
          })

          chainer
            .runSerially({ skipOnError: true })
            .success(function() { emitter.emit('success', null) })
            .error(function(err) { emitter.emit('error', err) })
        }
      })
    }).run()
  }

  Migrator.prototype.getUndoneMigrations = function(callback)  {
    var self = this

    var filterFrom = function(migrations, from, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isAfter(from, options) })
      callback && callback(null, result)
    }
    var filterTo = function(migrations, to, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isBefore(to, options) })
      callback && callback(null, result)
    }

    var migrationFiles = fs.readdirSync(this.options.path).filter(function(file) {
      return self.options.filesFilter.test(file)
    })

    var migrations = migrationFiles.map(function(file) {
      return new Migration(self, self.options.path + '/' + file)
    })

    migrations = migrations.sort(function(a,b){
      return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0])
    })

    if (this.options.from) {
      filterFrom(migrations, this.options.from, function(err, migrations) {
        if (self.options.to) {
          filterTo(migrations, self.options.to, callback)
        } else {
          callback && callback(null, migrations)
        }
      })
    } else {
      getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) {
        if (lastMigrationId) {
          filterFrom(migrations, lastMigrationId, function(err, migrations) {
            if (self.options.to) {
              filterTo(migrations, self.options.to, callback)
            } else {
              callback && callback(null, migrations)
            }
          }, { withoutEqual: true })
        } else {
          if (self.options.to) {
            filterTo(migrations, self.options.to, callback)
          } else {
            callback && callback(null, migrations)
          }
        }
      }).error(function(err) {
        callback && callback(err, null)
      })
    }
  }

  Migrator.prototype.findOrCreateSequelizeMetaDAO = function(syncOptions) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var storedDAO   = self.sequelize.daoFactoryManager.getDAO('SequelizeMeta')
        , SequelizeMeta = storedDAO

      if (!storedDAO) {
        SequelizeMeta = self.sequelize.define('SequelizeMeta', {
          from: DataTypes.STRING,
          to:   DataTypes.STRING
        }, {
          timestamps: false
        })
      }

      // force sync when model has newly created or if syncOptions are passed
      if (!storedDAO || syncOptions) {
        SequelizeMeta
          .sync(syncOptions || {})
          .success(function() { emitter.emit('success', SequelizeMeta) })
          .error(function(err) { emitter.emit('error', err) })
      } else {
        emitter.emit('success', SequelizeMeta)
      }
    }).run()
  }

  // private

  var getLastMigrationFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
        SequelizeMeta.find({ order: 'id DESC' }).success(function(meta) {
          emitter.emit('success', meta ? meta : null)
        }).error(function(err) { emitter.emit('error', err) })
      }).error(function(err) { emitter.emit(err) })
    }).run()
  }

  var getLastMigrationIdFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      getLastMigrationFromDatabase.call(self)
        .success(function(meta) {
          emitter.emit('success', meta ? meta.to : null)
        })
        .error(function(err) {
          emitter.emit('error', err)
        })
    }).run()
  }

  var getFormattedDateString = function(s) {
    var result = null

    try {
      result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
    } catch(e) {
      throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
    }

    return result
  }

  var stringToDate = function(s) {
    return moment(getFormattedDateString(s), "YYYYMMDDHHmmss")
  }

  var saveSuccessfulMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .create({ from: from.migrationId, to: to.migrationId })
        .success(callback)
    })
  }

  var deleteUndoneMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .find({ where: { from: from.migrationId.toString(), to: to.migrationId.toString() } })
        .success(function(meta) {
          meta.destroy().success(callback)
        })
    })
  }

  return Migrator
})()
<|endoftext|>