<commit_before>const request = require('request-promise');
const cheerio = require('cheerio');
const Promise = require('bluebird');
const async = require('asyncawait/async');
const wait = require('asyncawait/await'); // await is reserved in strict mode

const Course = require('./course');
const fetchInformation = require('./information');
const fetchRequisites = require('./requisites');
const teacher = require('./teacher');

const BASE_URL = 'http://buscacursos.uc.cl';

function query(qs) {
  return {
    uri: BASE_URL,
    method: 'GET',
    qs: qs,
    transform: function(body) {
      return cheerio.load(body);
    },
  };
}

function schedule(string) {
  const result = {};
  string.replace(/ /g,'').split(';').map(s => s.split(':')).map(S => {
    const days = S[0];
    const blocks = S[1];
    days.split('-').forEach(day => {
      result[day] = blocks.split(',').map(Number);
    });
  });
  return result;
}

module.exports = async(function(qs, options) {
  options = options || {};

  // Validate query
  if (!qs['cxml_semestre']) {
    throw (new Error(`missing 'cxml_semestre' query param.`));
  }

  // Make request
  const $ = wait(request(query(qs)));
  // Find results
  const last = $('body > div:nth-child(9)').children('table');
  if (!last.is('table')) {
    // Empty results
    return [];
  }

  // Parse results
  const table = last.children().toArray();

  // Filter rows and add school as extra 'td'
  var school = undefined;
  const rows = table.filter(tr => {
    tr = $(tr);
    if (tr.hasClass('resultadosRowImpar') || tr.hasClass('resultadosRowPar')) {
      // This row has a course
      tr.append(`<td>${school}</td>`);
      return true;
    } else if (tr.children().length === 1) {
      // This row is a header
      school = text(tr.children()[0]);
    }
    return false;
  });

  // Helper functions
  function text(node) {
    const t = $(node).text().trim();
    const temp = t.toLowerCase();
    return (temp === '(por asignar)' || temp === 'por fijar') ? null : t;
  }

  function boolean(node) {
    const t = text(node);
    return t && t.toUpperCase() === 'SI'
  }

  function number(node) {
    const t = text(node);
    return t ? Number(t) : null;
  }

  // Common properties for each result
  const time = qs['cxml_semestre'].split('-');
  const base = {
    year: Number(time[0]),
    period: Number(time[1]),
  }

  // Process table on parallel
  return wait(rows.map(tr => {
    const values = $(tr).children().toArray();
    const course = new Course(base);
    course.NRC = number(values[0]);
    course.initials = text(values[1]);
    course.droppable = boolean(values[2]);
    course.english = boolean(values[3]);
    course.section = number(values[4]);
    course.specialApproval = boolean(values[5]);
    course.name = text(values[6]);
    const teachers = text(values[7]);
    course.teachers = (teachers) ? teachers.split(',').map(name => {
      name = name.trim();
      return {
        name: name,
        photoURL: teacher.photoURL(name, course)
      };
    }) : [];
    course.credits = number(values[9]);
    course.vacancy = {
      total: number(values[10]),
      available: number(values[11]),
    };
    course.schedule = {};
    $('table', values[13]).children().each((_, subtr) => {
      const data = $(subtr).children().toArray();
      const type = text(data[1]);
      // Create if not present
      const shc = course.schedule[type] = course.schedule[type] || {
        modules: {},
        location: {
          campus: text(values[8]),
          place: text(data[2])
        }
      };
      const processed = schedule(text(data[0])); // { L: [1, 2, 3], W: [4, 5] }
      Object.keys(processed).forEach(day => {
        const blocks = processed[day];
        // Append if block type already exists
        if (shc.modules[day]) {
          shc.modules[day] = shc.modules[day].concat(blocks);
        } else {
          shc.modules[day] = blocks;
        }
      });
    });
    course.school = text(values[15]);

    // Additional requests
    const information = options.skipInformation ? null : fetchInformation(course).reflect();
    const requisites = options.skipRequisites ? null : fetchRequisites(course.initials).reflect();

    // Parallel requests
    return Promise.all([information, requisites])
      .then(results => results.map(r => (r && r.isFulfilled()) ? r.value() : null))
      .then(results => {
        course.information = results[0];
        course.requisites = results[1];
        return course;
      })
  }));
});
<commit_msg>filter teachers<commit_after>const request = require('request-promise');
const cheerio = require('cheerio');
const Promise = require('bluebird');
const async = require('asyncawait/async');
const wait = require('asyncawait/await'); // await is reserved in strict mode

const Course = require('./course');
const fetchInformation = require('./information');
const fetchRequisites = require('./requisites');
const teacher = require('./teacher');

const BASE_URL = 'http://buscacursos.uc.cl';

function query(qs) {
  return {
    uri: BASE_URL,
    method: 'GET',
    qs: qs,
    transform: function(body) {
      return cheerio.load(body);
    },
  };
}

function schedule(string) {
  const result = {};
  string.replace(/ /g,'').split(';').map(s => s.split(':')).map(S => {
    const days = S[0];
    const blocks = S[1];
    days.split('-').forEach(day => {
      result[day] = blocks.split(',').map(Number);
    });
  });
  return result;
}

function validTeacher(name) {
  return name.toLowerCase() !== '(sin profesores)';
}

module.exports = async(function(qs, options) {
  options = options || {};

  // Validate query
  if (!qs['cxml_semestre']) {
    throw (new Error(`missing 'cxml_semestre' query param.`));
  }

  // Make request
  const $ = wait(request(query(qs)));
  // Find results
  const last = $('body > div:nth-child(9)').children('table');
  if (!last.is('table')) {
    // Empty results
    return [];
  }

  // Parse results
  const table = last.children().toArray();

  // Filter rows and add school as extra 'td'
  var school = undefined;
  const rows = table.filter(tr => {
    tr = $(tr);
    if (tr.hasClass('resultadosRowImpar') || tr.hasClass('resultadosRowPar')) {
      // This row has a course
      tr.append(`<td>${school}</td>`);
      return true;
    } else if (tr.children().length === 1) {
      // This row is a header
      school = text(tr.children()[0]);
    }
    return false;
  });

  // Helper functions
  function text(node) {
    const t = $(node).text().trim();
    const temp = t.toLowerCase();
    return (temp === '(por asignar)' || temp === 'por fijar') ? null : t;
  }

  function boolean(node) {
    const t = text(node);
    return t && t.toUpperCase() === 'SI'
  }

  function number(node) {
    const t = text(node);
    return t ? Number(t) : null;
  }

  // Common properties for each result
  const time = qs['cxml_semestre'].split('-');
  const base = {
    year: Number(time[0]),
    period: Number(time[1]),
  }

  // Process table on parallel
  return wait(rows.map(tr => {
    const values = $(tr).children().toArray();
    const course = new Course(base);
    course.NRC = number(values[0]);
    course.initials = text(values[1]);
    course.droppable = boolean(values[2]);
    course.english = boolean(values[3]);
    course.section = number(values[4]);
    course.specialApproval = boolean(values[5]);
    course.name = text(values[6]);
    const teachers = text(values[7]);
    course.teachers = (teachers) ? teachers.split(',').filter(validTeacher).map(name => {
      name = name.trim();
      return {
        name: name,
        photoURL: teacher.photoURL(name, course)
      };
    }) : [];
    course.credits = number(values[9]);
    course.vacancy = {
      total: number(values[10]),
      available: number(values[11]),
    };
    course.schedule = {};
    $('table', values[13]).children().each((_, subtr) => {
      const data = $(subtr).children().toArray();
      const type = text(data[1]);
      // Create if not present
      const shc = course.schedule[type] = course.schedule[type] || {
        modules: {},
        location: {
          campus: text(values[8]),
          place: text(data[2])
        }
      };
      const processed = schedule(text(data[0])); // { L: [1, 2, 3], W: [4, 5] }
      Object.keys(processed).forEach(day => {
        const blocks = processed[day];
        // Append if block type already exists
        if (shc.modules[day]) {
          shc.modules[day] = shc.modules[day].concat(blocks);
        } else {
          shc.modules[day] = blocks;
        }
      });
    });
    course.school = text(values[15]);

    // Additional requests
    const information = options.skipInformation ? null : fetchInformation(course).reflect();
    const requisites = options.skipRequisites ? null : fetchRequisites(course.initials).reflect();

    // Parallel requests
    return Promise.all([information, requisites])
      .then(results => results.map(r => (r && r.isFulfilled()) ? r.value() : null))
      .then(results => {
        course.information = results[0];
        course.requisites = results[1];
        return course;
      })
  }));
});
<|endoftext|>


<commit_before>"use strict";
var postcss = require("postcss");

function getValueForProperty(parent, name) {
	var retValue;
	parent.walkDecls(new RegExp("^(?:-\\w+-)?" + name + "$"), function(decl) {
		retValue = decl.value;
	});
	return retValue;
}

function createFixupFlexboxDeclaration(propname, value, parent) {
	// remove vendor prefixes from names, values

	propname = postcss.vendor.unprefixed(propname);
	// value = postcss.vendor.unprefixed(value);

	var mappings = {
		"display": {
			valueMap: {
				"box": "flex",
				"flexbox": "flex",
				"inline-box": "inline-flex",
				"inline-flexbox": "inline-flex"
			}
		},
		"box-align": {
			newName: "align-items",
			valueMap: {
				"start": "flex-start",
				"end": "flex-end"
			}
		},
		"flex-direction": {
			valueMap: {
				"lr": "row",
				"rl": "row-reverse",
				"tb": "column",
				"bt": "column-reverse"
			}
		},
		"box-pack": {
			newName: "justify-content",
			valueMap: {
				"start": "flex-start",
				"end": "flex-end",
				"justify": "space-between"
			}
		},
		"box-ordinal-group": {
			newName: "order",
			valueMap: {}
		},
		"box-flex": {
			newName: "flex",
			valueMap: {}
		}
	};

	// 2009 => 2011
	mappings["flex-align"] = mappings["box-align"];
	// 2009 => 2011
	mappings["flex-order"] = mappings["box-ordinal-group"];

	if (propname in mappings) {
		if (value in mappings[propname].valueMap) {
			value = mappings[propname].valueMap[value];
		}
		propname = mappings[propname].newName || propname;
	}

	// some stuff is more complicated than a simple substitution..
	// box-flex:0 maps to 'none', other values need 'auto' appended - thanks to Daniel Holbert
	if (propname === "flex") {
		if (value === "0") {
			value = "none";
		}
	}

	// box-direction, box-orient is a bit of a mess - these two 2009 draft values turn into 2011's flex-direction, which again has different values for final spec
	if (propname === "box-direction" || propname === "box-orient") {
		var dir, orient;
		if (propname === "box-direction") {
			dir = value;
			orient = getValueForProperty(parent, "box-orient");
		} else {
			orient = value;
			dir = getValueForProperty(parent, "box-direction");
		}

		// horizontal,normal => row, vertical,normal => column. horizontal,reverse => row-reverse etc..
		// lr, rl etc are handled by the simpler mapping above, so we don't need to worry about those
		value = orient === "vertical" ? "column" : "row";
		if (dir === "reverse") {
			value += "-reverse";
		}
		propname = "flex-direction";
	}

	return {
		prop: propname,
		value: value
	};
}

module.exports = function(decl) {
	if (/^(?:-\w+-)?(?:box|flex)/.test(decl.prop) || (decl.prop === "display" && /^-\w+-/.test(decl.value))) {
		var result = createFixupFlexboxDeclaration(decl.prop, decl.value, decl.parent);
		if (result.prop !== decl.prop || result.value !== decl.value) {
			return result;
		}
	}
};
<commit_msg>fix #1<commit_after>"use strict";
var postcss = require("postcss");

function getValueForProperty(parent, name) {
	var retValue;
	parent.walkDecls(new RegExp("^(?:-\\w+-)?" + name + "$"), function(decl) {
		retValue = decl.value;
	});
	return retValue;
}

function createFixupFlexboxDeclaration(propname, value, parent) {
	// remove vendor prefixes from names, values

	propname = postcss.vendor.unprefixed(propname);

	var mappings = {
		"display": {
			valueMap: {
				"box": "flex",
				"flexbox": "flex",
				"inline-box": "inline-flex",
				"inline-flexbox": "inline-flex"
			}
		},
		"box-align": {
			newName: "align-items",
			valueMap: {
				"start": "flex-start",
				"end": "flex-end"
			}
		},
		"flex-direction": {
			valueMap: {
				"lr": "row",
				"rl": "row-reverse",
				"tb": "column",
				"bt": "column-reverse"
			}
		},
		"box-pack": {
			newName: "justify-content",
			valueMap: {
				"start": "flex-start",
				"end": "flex-end",
				"justify": "space-between"
			}
		},
		"box-ordinal-group": {
			newName: "order",
			valueMap: {}
		},
		"box-flex": {
			newName: "flex",
			valueMap: {}
		}
	};

	// 2009 => 2011
	mappings["flex-align"] = mappings["box-align"];
	// 2009 => 2011
	mappings["flex-order"] = mappings["box-ordinal-group"];

	if (propname in mappings) {
		value = postcss.vendor.unprefixed(value);
		if (value in mappings[propname].valueMap) {
			value = mappings[propname].valueMap[value];
		}
		propname = mappings[propname].newName || propname;
	}

	// some stuff is more complicated than a simple substitution..
	// box-flex:0 maps to 'none', other values need 'auto' appended - thanks to Daniel Holbert
	if (propname === "flex" && value === "0") {
		value = "none";
	}

	// box-direction, box-orient is a bit of a mess - these two 2009 draft values turn into 2011's flex-direction, which again has different values for final spec
	if (propname === "box-direction" || propname === "box-orient") {
		value = postcss.vendor.unprefixed(value);
		var dir, orient;
		if (propname === "box-direction") {
			dir = value;
			orient = getValueForProperty(parent, "box-orient");
		} else {
			orient = value;
			dir = getValueForProperty(parent, "box-direction");
		}

		// horizontal,normal => row, vertical,normal => column. horizontal,reverse => row-reverse etc..
		// lr, rl etc are handled by the simpler mapping above, so we don't need to worry about those
		value = orient === "vertical" ? "column" : "row";
		if (dir === "reverse") {
			value += "-reverse";
		}
		propname = "flex-direction";
	}

	return {
		prop: propname,
		value: value
	};
}

module.exports = function(decl) {
	if (/^(?:-\w+-)?(?:box|flex)/.test(decl.prop) || (decl.prop === "display" && /^-\w+-/.test(decl.value))) {
		var result = createFixupFlexboxDeclaration(decl.prop, decl.value, decl.parent);
		if (result.prop !== decl.prop || result.value !== decl.value) {
			return result;
		}
	}
};
<|endoftext|>


<commit_before>(function($) {
  $.declare('floater', {
    defaults: {
      topPadding: 0,
      stopPoint: -1,
      startPoint: -1,
      relative: false,
      offsetX: 0,
      offsetY: 0
    },

    init: function() {
      this.enabled = true;
      this.height = this.el.outerHeight(true);
      this.resetStyles = {
        position: this.el.css('position'),
        top: this.el.css('top'),
        left: this.el.css('left'),
        width: this.el.css('width')
      };

      var offset = this.el.offset();
      this.startPoint = this.startPoint == -1 ? (offset.top - parseInt(this.topPadding, 10)) : this.startPoint;

      if (typeof this.stopPoint == 'object' && this.stopPoint.length) {
        this.stopPoint = this.stopPoint.offset().top;
      }

      this.floating = false;
      $(window).on('scroll', this.proxy(this.onScroll));
      this.onScroll();
    },

    setStartPoint: function(point) {
      this.startPoint = point;
    },

    setStopPoint: function(point) {
      this.stopPoint = point;
    },

    createPlaceholder: function() {
      var classes = this.el[0].className || '';
      return $('<div/>')
        .addClass('floater-placeholder ' + classes)
        .css({
          width: this.el.css('width'),
          height: this.el.css('height'),
          visibility: 'hidden'
        });
    },

    startFloat: function() {
      if (this.floating) {
        return;
      }
      this.floating = true;
      var offset = this.el.offset();
      this.el.before(this.createPlaceholder());
      this.el.css({
        position: 'fixed',
        top: this.topPadding,
        left: offset.left + this.offsetX,
        width: this.el.css('width')
      });
      this.emit('floatStart');
    },

    stopFloat: function(posY) {
      if (!this.floating) {
        return;
      }
      this.floating = false;
      this.el.prev().remove();
      this.el.css({
        position: '',
        top: '',
        left: '',
        width: ''
      });
      this.emit('floatStop');
    },

    onScroll: function(e) {
      if (!this.enabled) {
        return;
      }
      var scrollY = $(window).scrollTop();
      if (scrollY > this.startPoint) {
        if (this.stopPoint != -1 && (scrollY + this.height) > this.stopPoint) {
          if (this.floating) {
            this.stopFloat(this.stopPoint - this.height);
          }
        } else if (!this.floating) {
          this.startFloat();
        }
      } else if (this.floating && scrollY < this.startPoint) { //top of screen
        this.stopFloat(this.resetStyles.top);
      }
    },

    on: function() {
      this.enabled = true;
      this.onScroll();
    },

    off: function() {
      this.stopFloat();
      this.enabled = false;
    }
  });

})(window.jQuery || window.Zepto);
<commit_msg>[lib] fix freezing on stop<commit_after>(function($) {
  $.declare('floater', {
    defaults: {
      topPadding: 0,
      stopPoint: -1,
      startPoint: -1,
      relative: false,
      offsetX: 0,
      offsetY: 0
    },

    init: function() {
      this.enabled = true;
      this.height = this.el.outerHeight(true);
      this.resetStyles = {
        position: this.el.css('position'),
        top: this.el.css('top'),
        left: this.el.css('left'),
        width: this.el.css('width')
      };

      var offset = this.el.offset();
      this.startPoint = this.startPoint == -1 ? (offset.top - parseInt(this.topPadding, 10)) : this.startPoint;

      if (typeof this.stopPoint == 'object' && this.stopPoint.length) {
        this.stopPoint = this.stopPoint.offset().top;
      }

      this.floating = false;
      $(window).on('scroll', this.proxy(this.onScroll));
      this.onScroll();
    },

    setStartPoint: function(point) {
      this.startPoint = point;
    },

    setStopPoint: function(point) {
      this.stopPoint = point;
    },

    createPlaceholder: function() {
      var classes = this.el[0].className || '';
      return $('<div/>')
        .addClass('floater-placeholder ' + classes)
        .css({
          width: this.el.css('width'),
          height: this.el.css('height'),
          visibility: 'hidden'
        });
    },

    startFloat: function() {
      if (this.floating) {
        return;
      }
      this.floating = true;
      var offset = this.el.offset();
      this.el.before(this.createPlaceholder());
      this.el.css({
        position: 'fixed',
        top: this.topPadding,
        left: offset.left + this.offsetX,
        width: this.el.css('width')
      });
      this.emit('floatStart');
    },

    stopFloat: function(y) {
      if (!this.floating) {
        return;
      }
      this.floating = false;
      this.el.prev().remove();
      this.el.css({
        position: (y) ? 'absolute' : '',
        top: y || '',
        left: '',
        width: (y) ? this.el.css('width') : ''
      });
      this.emit('floatStop');
    },

    onScroll: function(e) {
      if (!this.enabled) {
        return;
      }
      var scrollY = $(window).scrollTop();
      if (scrollY > this.startPoint) {
        if (this.stopPoint != -1 && (scrollY + this.height) > this.stopPoint) {
          if (this.floating) {
            this.stopFloat(this.stopPoint - this.height);
          }
        } else if (!this.floating) {
          this.startFloat();
        }
      } else if (this.floating && scrollY < this.startPoint) { //top of screen
        this.stopFloat();
      }
    },

    on: function() {
      this.enabled = true;
      this.onScroll();
    },

    off: function() {
      this.stopFloat();
      this.enabled = false;
    }
  });

})(window.jQuery || window.Zepto);
<|endoftext|>


<commit_before>// Load in dependencies
var util = require('util');
var async = require('async');
var extend = require('obj-extend');
var shell = require('shelljs');

var Command = require('commander-completion').Command;

function Foundry() {
  // Set up program
  Command.apply(this, arguments);

  // Bind our commands
  this.bindCommands();
}
util.inherits(Foundry, Command);
extend(Foundry.prototype, {
  name: 'foundry',
  bindCommands: function () {
    // Set up commands
    // TODO: Following iterations ($EDITOR for message, try to update changelog)
    var program = this;
    var that = this;
    program
      .command('release <version>')
      .description('Set version for package.json, bower, component, git tag, and setup.py and publish to corresponding registries')
      .action(function callRelease (version, argv) {
        that.release(version, function handleError (err) {
          if (err) {
            throw err;
          }
        });
      });

    // TODO: Move this into its own node module
    program
      .command('completion')
      .description('Get potential completions for a command. Looks for `COMP_CWORD`, `COMP_LINE`, `COMP_POINT`.')
      .action(function completeLine (/* word1, word2, ..., argv */) {
        // Run completion against environment variables
        program.complete({
          line: process.env.COMP_LINE,
          cursor: process.env.COMP_POINT
        });
      });
  },

  release: function (version, cb) {
    // DEV: This is directly mimicking behavior from `git-release`
    var that = this;
    var params = {
      version: version,
      // TODO: Load this from a file (e.g. ~/.config/foundry or ~/.foundry/config.json
      config: {}
    };
    async.series([
      function setVersionFn (cb) {
        that.emit('setVersion#before');
        that.setVersion(params, cb);
      },
      function registerFn (cb) {
        that.emit('setVersion#after');
        // Only run register if we are at the 'register' semver (e.g. 0.1.0)
        // TODO: Make this a config setting
        if (version === '0.1.0') {
          that.emit('register#before');
          that.register(params, cb);
        } else {
          process.nextTick(cb);
        }
      },
      function publishFn (cb) {
        if (version === '0.1.0') {
          that.emit('register#after');
        }
        that.emit('publish#before');
        that.publish(params, cb);
      },
      function afterPublishFn (cb) {
        that.emit('publish#after');
        cb();
      }
    ], cb);
  },

  getReleaseLibs: function (cb) {
    // TODO: Use readdir + require
    var releaseLibs = [
      require('./release/git'),
      require('./release/npm'),
      require('./release/bower'),
      require('./release/component'),
      require('./release/python')
    ];
    cb(null, releaseLibs);
  },

  setVersion: function (params, cb) {
    this.getReleaseLibs(function (err, releaseLibs) {
      if (err) { return cb(err); }
      async.forEach(releaseLibs, function setVersionFn (release, cb) {
        if (release.setVersion) {
          release.setVersion(params, cb);
        } else {
          process.nextTick(cb);
        }
      }, cb);
    });
  },
  register: function (params, cb) {
    this.getReleaseLibs(function (err, releaseLibs) {
      if (err) { return cb(err); }
      async.forEach(releaseLibs, function registerFn (release, cb) {
        if (release.register) {
          release.register(params, cb);
        } else {
          process.nextTick(cb);
        }
      }, cb);
    });
  },
  publish: function (params, cb) {
    this.getReleaseLibs(function (err, releaseLibs) {
      if (err) { return cb(err); }
      async.forEach(releaseLibs, function publishFn (release, cb) {
        if (release.publish) {
          release.publish(params, cb);
        } else {
          process.nextTick(cb);
        }
      }, cb);
    });
  }
});
module.exports = Foundry;
<commit_msg>Correcting typo<commit_after>// Load in dependencies
var util = require('util');
var async = require('async');
var extend = require('obj-extend');
var shell = require('shelljs');

var Command = require('commander-completion').Command;

function Foundry() {
  // Set up program
  Command.apply(this, arguments);

  // Bind our commands
  this.bindCommands();
}
util.inherits(Foundry, Command);
extend(Foundry.prototype, {
  name: 'foundry',
  bindCommands: function () {
    // Set up commands
    // TODO: Following iterations ($EDITOR for message, try to update changelog)
    var program = this;
    var that = this;
    program
      .command('release <version>')
      .description('Set version for package.json, bower, component, git tag, and setup.py and publish to corresponding registries')
      .action(function callRelease (version, argv) {
        that.release(version, function handleError (err) {
          if (err) {
            throw err;
          }
        });
      });

    // TODO: Move this into its own node module
    program
      .command('completion')
      .description('Get potential completions for a command. Looks for `COMP_CWORD`, `COMP_LINE`, `COMP_POINT`.')
      .action(function completeLine (/* word1, word2, ..., argv */) {
        // Run completion against environment variables
        program.complete({
          line: process.env.COMP_LINE,
          cursor: process.env.COMP_POINT
        });
      });
  },

  release: function (version, cb) {
    // DEV: This is directly mimicking behavior from `git-release`
    var that = this;
    var params = {
      version: version,
      // TODO: Load this from a file (e.g. ~/.config/foundry or ~/.foundry/config.json)
      config: {}
    };
    async.series([
      function setVersionFn (cb) {
        that.emit('setVersion#before');
        that.setVersion(params, cb);
      },
      function registerFn (cb) {
        that.emit('setVersion#after');
        // Only run register if we are at the 'register' semver (e.g. 0.1.0)
        // TODO: Make this a config setting
        if (version === '0.1.0') {
          that.emit('register#before');
          that.register(params, cb);
        } else {
          process.nextTick(cb);
        }
      },
      function publishFn (cb) {
        if (version === '0.1.0') {
          that.emit('register#after');
        }
        that.emit('publish#before');
        that.publish(params, cb);
      },
      function afterPublishFn (cb) {
        that.emit('publish#after');
        cb();
      }
    ], cb);
  },

  getReleaseLibs: function (cb) {
    // TODO: Use readdir + require
    var releaseLibs = [
      require('./release/git'),
      require('./release/npm'),
      require('./release/bower'),
      require('./release/component'),
      require('./release/python')
    ];
    cb(null, releaseLibs);
  },

  setVersion: function (params, cb) {
    this.getReleaseLibs(function (err, releaseLibs) {
      if (err) { return cb(err); }
      async.forEach(releaseLibs, function setVersionFn (release, cb) {
        if (release.setVersion) {
          release.setVersion(params, cb);
        } else {
          process.nextTick(cb);
        }
      }, cb);
    });
  },
  register: function (params, cb) {
    this.getReleaseLibs(function (err, releaseLibs) {
      if (err) { return cb(err); }
      async.forEach(releaseLibs, function registerFn (release, cb) {
        if (release.register) {
          release.register(params, cb);
        } else {
          process.nextTick(cb);
        }
      }, cb);
    });
  },
  publish: function (params, cb) {
    this.getReleaseLibs(function (err, releaseLibs) {
      if (err) { return cb(err); }
      async.forEach(releaseLibs, function publishFn (release, cb) {
        if (release.publish) {
          release.publish(params, cb);
        } else {
          process.nextTick(cb);
        }
      }, cb);
    });
  }
});
module.exports = Foundry;
<|endoftext|>


<commit_before>require("es6-shim");

var IS_TEST_ENV = global.IS_TEST_ENV || false;
var fs = require("fs");
var Emitter = require("events").EventEmitter;
var Promise = require("es6-promise").Promise;
var Pin, io, mraa;

try {
  if (IS_TEST_ENV) {
    io = mraa = require("../test/mraa-mock.js");
  } else {
    io = mraa = require("mraa");
  }
  Pin = require("../lib/mraa.js");
} catch (e) {
  IS_TEST_ENV = false;
  Pin = require("../lib/pin.js");
}

var modes = Object.freeze({
  INPUT: 0,
  OUTPUT: 1,
  ANALOG: 2,
  PWM: 3,
  SERVO: 4
});

var pinModes = [
  { modes: [] },
  { modes: [] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 2], analogChannel: 0 },
  { modes: [0, 1, 2], analogChannel: 1 },
  { modes: [0, 1, 2], analogChannel: 2 },
  { modes: [0, 1, 2], analogChannel: 3 },
  { modes: [0, 1, 2], analogChannel: 4 },
  { modes: [0, 1, 2], analogChannel: 5 }
];

// mraa does not support PWM on pin 10 and 11
// on the Edison (which only has 4 PWM channels),
// so we need to redefine those supported modes
// without PWM or SERVO.
// See discussion here:
// https://github.com/intel-iot-devkit/mraa/issues/52#issuecomment-65370890
//
if (mraa.getPlatformType() === 2) {
  pinModes[10].modes = [0, 1];
  pinModes[11].modes = [0, 1];
}

var platforms = [
  "Intel Galileo 1",
  "Intel Galileo 2",
  "Intel Edison"
];

var priv = new Map();
var boards = [];
var reporting = [];
// Experimental
// var i2cQueue = [];

/**
 * Several approaches where considered and attempted for
 * efficiently reading the value of GPIO and AIO pins.
 *
 * fs.watch: does not report changes made by the system
 *
 * gaze: does not report changes made by the system
 *
 * setImmediate: hogs the CPU
 *
 */
function read() {
  if (read.isReading) {
    return;
  }
  read.isReading = true;
  read.interval = setInterval(function() {
    var board, request, state;

    // TODO: Limit to one read cycle per ms?
    // Patches welcome for better approaches
    // that maintain the semantics.

    if (boards.length) {
      board = boards[0];

      if (reporting.length) {
        reporting.forEach(function(report, gpio) {
          if (mraa) {
            var pin = board.pins[report.index];
            if (pin && pin.read) {
              processRead(board, report, pin.read());
            }
          } else {
            fs.readFile(report.path, "utf8", function(err, value) {
              if (!err) {
                processRead(board, report, value);
              }
            });
          }
        });
      }

      // Experimental
      // if (i2cQueue.length) {
      //   state = priv.get(board);
      //   request = i2cQueue.shift();
      //   state.i2c.address(request.address);
      //   state.i2c.write(request.buffer.toString("ascii"));
      // }
    }
  }, 1);
}

function processRead(board, report, value) {
  value = +value;

  if (report.scale) {
    value = report.scale(value);
  }

  board.pins[report.index].value = value;
  board.emit(report.event, value);
}

function ToPinIndex(pin) {
  var offset = pin[0] === "A" ? 14 : 0;
  return ((pin + "").replace("A", "") | 0) + offset;
}

function Galileo(opts) {
  Emitter.call(this);

  if (!(this instanceof Galileo)) {
    return new Galileo(opts);
  }
  var awaiting = [];
  var state = {
    i2c: null
  };

  priv.set(this, state);

  this.name = "Galileo-IO" + (mraa && " (" + platforms[mraa.getPlatformType()] + ")");
  this.isReady = false;

  // TODO:
  //
  // Once raw sysfs/gpio support is dropped, the
  // use of Promises will no longer be necessary
  //
  this.pins = pinModes.map(function(pin, index) {
    pin.addr = typeof pin.analogChannel === "number" ?
      "A" + pin.analogChannel : index;

    var gpio = new Pin(pin);

    awaiting.push(
      new Promise(function(resolve) {
        gpio.on("ready", function() {
          resolve();
        });
      })
    );

    return gpio;
  }, this);

  this.analogPins = this.pins.slice(14).map(function(pin, i) {
    return i;
  });

  boards[0] = this;

  // Connected to the device by default.
  process.nextTick(function() {
    this.emit("connect");
  }.bind(this));

  // The "ready event" is needed to signal to Johnny-Five that
  // communication with the Arduino pinouts is ready.
  Promise.all(awaiting).then(function() {
    this.isReady = true;
    this.emit("ready");
  }.bind(this));
}

Galileo.reset = function() {
  reporting.length = 0;
  read.isReading = false;
  clearInterval(read.interval);
};

Galileo.prototype = Object.create(Emitter.prototype, {
  constructor: {
    value: Galileo
  },
  MODES: {
    value: modes
  },
  HIGH: {
    value: 1
  },
  LOW: {
    value: 0
  }
});

Galileo.prototype.pinMode = function(pin, mode) {
  var pinIndex;
  var gpioMode;
  var direction;

  gpioMode = mode = +mode;

  // Normalize ANALOG (input) mode to INPUT
  if (mode === 2) {
    gpioMode = 0;

    if (typeof pin === "number") {
      pin = "A" + pin;
    }
  }

  pinIndex = ToPinIndex(pin);

  // For Analog Input, set the GPIO MUX direction to "out"
  // Digital Input is set "in"
  // Digital Output is set "out"
  direction = gpioMode ? "out" : (mode === 2 ? "out" : "in");

  this.pins[pinIndex].mode = mode;
  this.pins[pinIndex].isPwm = mode === 3 || mode === 4;

  if (!mraa) {
    this.pins[pinIndex].direction = direction;

    // For Analog Input, write `0` to the GPIO MUX
    // to connect the analog pin to the ADC chip
    if (mode === 2) {
      this.pins[pinIndex].write(0);
    }
  }

  return this;
};

Galileo.prototype.analogRead = function(pin, handler) {
  var pinIndex;
  var gpio;
  var alias;
  var event;

  // Convert numeric analog pin numbers to "A*" format
  if (typeof pin === "number") {
    pin = "A" + pin;
  }

  pinIndex = ToPinIndex(pin);
  gpio = this.pins[pinIndex].gpio;
  alias = this.pins[pinIndex].analogChannel;
  event = "analog-read-" + alias;

  if (this.pins[pinIndex].mode !== this.MODES.ANALOG) {
    this.pinMode(pin, this.MODES.ANALOG);
  }

  // The sysfs port will have a 12-bit value of 0-4095,
  // the scale function will shift the value two bits to
  // right to produce a 10-bit value which matches Arduino
  // ADC read values.
  reporting[+gpio] = {
    alias: alias,
    event: event,
    index: pinIndex,
    path: mraa ? null : "/sys/bus/iio/devices/iio:device0/in_voltage" + alias + "_raw",
    scale: function(raw) {
      if (mraa) {
        return raw;
      }
      return raw >>> 2;
    }
  };

  this.on(event, handler);

  read();

  return this;
};

Galileo.prototype.digitalRead = function(pin, handler) {
  var pinIndex = ToPinIndex(pin);
  var gpio = this.pins[pinIndex].gpio;
  var event = "digital-read-" + pin;

  if (this.pins[pinIndex].mode !== this.MODES.INPUT) {
    this.pinMode(pin, this.MODES.INPUT);
  }

  reporting[+gpio] = {
    event: event,
    index: pinIndex,
    path: mraa ? null : this.pins[pinIndex].paths.value
  };

  this.on(event, handler);

  read();

  return this;
};

Galileo.prototype.analogWrite = function(pin, value) {
  var pinIndex = ToPinIndex(pin);

  if (this.pins[pinIndex].mode !== this.MODES.PWM) {
    this.pinMode(pin, this.MODES.PWM);
  }

  this.pins[pinIndex].write(value);

  return this;
};

Galileo.prototype.digitalWrite = function(pin, value) {
  var pinIndex = ToPinIndex(pin);

  if (this.pins[pinIndex].mode !== this.MODES.OUTPUT) {
    this.pinMode(pin, this.MODES.OUTPUT);
  }

  this.pins[pinIndex].write(value);

  return this;
};

Galileo.prototype.servoWrite = function(pin, value) {
  var pinIndex = ToPinIndex(pin);

  if (this.pins[pinIndex].mode !== this.MODES.SERVO) {
    this.pinMode(pin, this.MODES.SERVO);
  }

  this.pins[pinIndex].write(value);

  return this;
};

// By default, these parts are not supported
// by the fs based implementation. This will
// soon be deprecated.
[
  "pulseIn",
  "pulseOut",
  "queryPinState",
  "_sendOneWireRequest",
  "_sendOneWireSearch",
  "sendI2CWriteRequest",
  "sendI2CReadRequest",
  "sendI2CConfig",
  "sendOneWireWriteAndRead",
  "sendOneWireDelay",
  "sendOneWireDelay",
  "sendOneWireReset",
  "sendOneWireRead",
  "sendOneWireSearch",
  "sendOneWireAlarmsSearch",
  "sendOneWireConfig",
  "stepperConfig",
  "stepperStep"
].forEach(function(method) {
  Galileo.prototype[method] = function() {
    throw method + " is not yet implemented.";
  };
});

if (mraa) {

  // Bus determination inferred via
  // https://github.com/intel-iot-devkit/mraa/commit/995565b2e1c3489810c66953e30ca07a83718c51
  // https://github.com/intel-iot-devkit/mraa/commit/dd89adc47b75e3d0f237d07884e0c69faecc6d9c
  //
  // 0 => 0
  // 1 => 0
  // 2 => 6
  //
  var bus = [0, 0, 6][mraa.getPlatformType()];

  // Map to Board.prototype.sendI2CConfig
  Galileo.prototype.i2cConfig = function(delay) {
    var state = priv.get(this);
    // Initialize the I2C interface if none currently exists
    if (!state.i2c) {
      state.i2c = new mraa.I2c(bus);
    }
  };

  // Map to Board.prototype.sendI2CWriteRequest
  Galileo.prototype.i2cWrite = function(address, cmdRegOrData, inBytes) {
    /**
     * cmdRegOrData:
     * [... arbitrary bytes]
     *
     * or
     *
     * cmdRegOrData, inBytes:
     * command [, ...]
     *
     */
    var state = priv.get(this);
    var buffer;

    this.i2cConfig();

    // If i2cWrite was used for an i2cWriteReg call...
    if (arguments.length === 3 &&
        !Array.isArray(cmdRegOrData) &&
        !Array.isArray(inBytes)) {

      return this.i2cWriteReg(address, cmdRegOrData, inBytes);
    }

    // Fix arguments if called with Firmata.js API
    if (arguments.length === 2) {
      if (Array.isArray(cmdRegOrData)) {
        inBytes = cmdRegOrData.slice();
        cmdRegOrData = inBytes.shift();
      } else {
        inBytes = [];
      }
    }

    buffer = new Buffer([cmdRegOrData].concat(inBytes));

    // Only write if bytes provided
    if (buffer.length) {
      // Experimental:
      //
      //  - Throttling writes by making them async
      //
      // i2cQueue.push({
      //   address: address,
      //   buffer: buffer
      // });

      state.i2c.address(address);
      state.i2c.write(buffer.toString("ascii"));

      // Certain devices may not allow a batch value write.
      // for (var i = 0; i < buffer.length; i++) {
      //   state.i2c.write(buffer.toString("ascii", i, i + 1));
      // }
    }

    return this;
  };

  Galileo.prototype.i2cWriteReg = function(address, register, value) {
    var state = priv.get(this);

    this.i2cConfig();

    state.i2c.address(address);
    state.i2c.writeReg(register, value);

    return this;
  };

  // Map to Board.prototype.sendI2CReadRequest
  Galileo.prototype.i2cRead = function(address, cmdOrBytes, bytesToRead, callback) {
    var state = priv.get(this);

    this.i2cConfig();

    // Fix arguments if called with Firmata.js API
    if (arguments.length === 3 &&
        typeof cmdOrBytes === "number" &&
        typeof bytesToRead === "function") {
      callback = bytesToRead;
      bytesToRead = cmdOrBytes;
      cmdOrBytes = null;
    }

    callback = typeof callback === "function" ? callback : function() {};

    if (cmdOrBytes) {
      this.i2cWrite(address, cmdOrBytes);
    }

    this.once("I2C-reply-" + address, callback);

    process.nextTick(function() {
      state.i2c.address(address);

      var data = new Buffer(state.i2c.read(bytesToRead));
      var values = [];

      for (var i = 0; i < bytesToRead; i++) {
        values.push(data.readUInt8(i));
      }

      this.emit("I2C-reply-" + address, values);
    }.bind(this));

    return this;
  };

  // Necessary for Firmata.js compatibility.
  Galileo.prototype.sendI2CWriteRequest = Galileo.prototype.i2cWrite;
  Galileo.prototype.sendI2CReadRequest = Galileo.prototype.i2cRead;
  Galileo.prototype.sendI2CConfig = Galileo.prototype.i2cConfig;
}

if (IS_TEST_ENV) {
  Galileo.__io = io;
  Galileo.__reporting = reporting;
}

module.exports = Galileo;
// http://wiki.ros.org/IntelGalileo/IntelGalileoPin
<commit_msg>Only check for Edison + mraa when present.<commit_after>require("es6-shim");

var IS_TEST_ENV = global.IS_TEST_ENV || false;
var fs = require("fs");
var Emitter = require("events").EventEmitter;
var Promise = require("es6-promise").Promise;
var Pin, io, mraa;

try {
  if (IS_TEST_ENV) {
    io = mraa = require("../test/mraa-mock.js");
  } else {
    io = mraa = require("mraa");
  }
  Pin = require("../lib/mraa.js");
} catch (e) {
  IS_TEST_ENV = false;
  Pin = require("../lib/pin.js");
}

var modes = Object.freeze({
  INPUT: 0,
  OUTPUT: 1,
  ANALOG: 2,
  PWM: 3,
  SERVO: 4
});

var pinModes = [
  { modes: [] },
  { modes: [] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 3, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 4] },
  { modes: [0, 1, 2], analogChannel: 0 },
  { modes: [0, 1, 2], analogChannel: 1 },
  { modes: [0, 1, 2], analogChannel: 2 },
  { modes: [0, 1, 2], analogChannel: 3 },
  { modes: [0, 1, 2], analogChannel: 4 },
  { modes: [0, 1, 2], analogChannel: 5 }
];

// mraa does not support PWM on pin 10 and 11
// on the Edison (which only has 4 PWM channels),
// so we need to redefine those supported modes
// without PWM or SERVO.
// See discussion here:
// https://github.com/intel-iot-devkit/mraa/issues/52#issuecomment-65370890
//
if (mraa && mraa.getPlatformType() === 2) {
  pinModes[10].modes = [0, 1];
  pinModes[11].modes = [0, 1];
}

var platforms = [
  "Intel Galileo 1",
  "Intel Galileo 2",
  "Intel Edison"
];

var priv = new Map();
var boards = [];
var reporting = [];
// Experimental
// var i2cQueue = [];

/**
 * Several approaches where considered and attempted for
 * efficiently reading the value of GPIO and AIO pins.
 *
 * fs.watch: does not report changes made by the system
 *
 * gaze: does not report changes made by the system
 *
 * setImmediate: hogs the CPU
 *
 */
function read() {
  if (read.isReading) {
    return;
  }
  read.isReading = true;
  read.interval = setInterval(function() {
    var board, request, state;

    // TODO: Limit to one read cycle per ms?
    // Patches welcome for better approaches
    // that maintain the semantics.

    if (boards.length) {
      board = boards[0];

      if (reporting.length) {
        reporting.forEach(function(report, gpio) {
          if (mraa) {
            var pin = board.pins[report.index];
            if (pin && pin.read) {
              processRead(board, report, pin.read());
            }
          } else {
            fs.readFile(report.path, "utf8", function(err, value) {
              if (!err) {
                processRead(board, report, value);
              }
            });
          }
        });
      }

      // Experimental
      // if (i2cQueue.length) {
      //   state = priv.get(board);
      //   request = i2cQueue.shift();
      //   state.i2c.address(request.address);
      //   state.i2c.write(request.buffer.toString("ascii"));
      // }
    }
  }, 1);
}

function processRead(board, report, value) {
  value = +value;

  if (report.scale) {
    value = report.scale(value);
  }

  board.pins[report.index].value = value;
  board.emit(report.event, value);
}

function ToPinIndex(pin) {
  var offset = pin[0] === "A" ? 14 : 0;
  return ((pin + "").replace("A", "") | 0) + offset;
}

function Galileo(opts) {
  Emitter.call(this);

  if (!(this instanceof Galileo)) {
    return new Galileo(opts);
  }
  var awaiting = [];
  var state = {
    i2c: null
  };

  priv.set(this, state);

  this.name = "Galileo-IO" + (mraa && " (" + platforms[mraa.getPlatformType()] + ")");
  this.isReady = false;

  // TODO:
  //
  // Once raw sysfs/gpio support is dropped, the
  // use of Promises will no longer be necessary
  //
  this.pins = pinModes.map(function(pin, index) {
    pin.addr = typeof pin.analogChannel === "number" ?
      "A" + pin.analogChannel : index;

    var gpio = new Pin(pin);

    awaiting.push(
      new Promise(function(resolve) {
        gpio.on("ready", function() {
          resolve();
        });
      })
    );

    return gpio;
  }, this);

  this.analogPins = this.pins.slice(14).map(function(pin, i) {
    return i;
  });

  boards[0] = this;

  // Connected to the device by default.
  process.nextTick(function() {
    this.emit("connect");
  }.bind(this));

  // The "ready event" is needed to signal to Johnny-Five that
  // communication with the Arduino pinouts is ready.
  Promise.all(awaiting).then(function() {
    this.isReady = true;
    this.emit("ready");
  }.bind(this));
}

Galileo.reset = function() {
  reporting.length = 0;
  read.isReading = false;
  clearInterval(read.interval);
};

Galileo.prototype = Object.create(Emitter.prototype, {
  constructor: {
    value: Galileo
  },
  MODES: {
    value: modes
  },
  HIGH: {
    value: 1
  },
  LOW: {
    value: 0
  }
});

Galileo.prototype.pinMode = function(pin, mode) {
  var pinIndex;
  var gpioMode;
  var direction;

  gpioMode = mode = +mode;

  // Normalize ANALOG (input) mode to INPUT
  if (mode === 2) {
    gpioMode = 0;

    if (typeof pin === "number") {
      pin = "A" + pin;
    }
  }

  pinIndex = ToPinIndex(pin);

  // For Analog Input, set the GPIO MUX direction to "out"
  // Digital Input is set "in"
  // Digital Output is set "out"
  direction = gpioMode ? "out" : (mode === 2 ? "out" : "in");

  this.pins[pinIndex].mode = mode;
  this.pins[pinIndex].isPwm = mode === 3 || mode === 4;

  if (!mraa) {
    this.pins[pinIndex].direction = direction;

    // For Analog Input, write `0` to the GPIO MUX
    // to connect the analog pin to the ADC chip
    if (mode === 2) {
      this.pins[pinIndex].write(0);
    }
  }

  return this;
};

Galileo.prototype.analogRead = function(pin, handler) {
  var pinIndex;
  var gpio;
  var alias;
  var event;

  // Convert numeric analog pin numbers to "A*" format
  if (typeof pin === "number") {
    pin = "A" + pin;
  }

  pinIndex = ToPinIndex(pin);
  gpio = this.pins[pinIndex].gpio;
  alias = this.pins[pinIndex].analogChannel;
  event = "analog-read-" + alias;

  if (this.pins[pinIndex].mode !== this.MODES.ANALOG) {
    this.pinMode(pin, this.MODES.ANALOG);
  }

  // The sysfs port will have a 12-bit value of 0-4095,
  // the scale function will shift the value two bits to
  // right to produce a 10-bit value which matches Arduino
  // ADC read values.
  reporting[+gpio] = {
    alias: alias,
    event: event,
    index: pinIndex,
    path: mraa ? null : "/sys/bus/iio/devices/iio:device0/in_voltage" + alias + "_raw",
    scale: function(raw) {
      if (mraa) {
        return raw;
      }
      return raw >>> 2;
    }
  };

  this.on(event, handler);

  read();

  return this;
};

Galileo.prototype.digitalRead = function(pin, handler) {
  var pinIndex = ToPinIndex(pin);
  var gpio = this.pins[pinIndex].gpio;
  var event = "digital-read-" + pin;

  if (this.pins[pinIndex].mode !== this.MODES.INPUT) {
    this.pinMode(pin, this.MODES.INPUT);
  }

  reporting[+gpio] = {
    event: event,
    index: pinIndex,
    path: mraa ? null : this.pins[pinIndex].paths.value
  };

  this.on(event, handler);

  read();

  return this;
};

Galileo.prototype.analogWrite = function(pin, value) {
  var pinIndex = ToPinIndex(pin);

  if (this.pins[pinIndex].mode !== this.MODES.PWM) {
    this.pinMode(pin, this.MODES.PWM);
  }

  this.pins[pinIndex].write(value);

  return this;
};

Galileo.prototype.digitalWrite = function(pin, value) {
  var pinIndex = ToPinIndex(pin);

  if (this.pins[pinIndex].mode !== this.MODES.OUTPUT) {
    this.pinMode(pin, this.MODES.OUTPUT);
  }

  this.pins[pinIndex].write(value);

  return this;
};

Galileo.prototype.servoWrite = function(pin, value) {
  var pinIndex = ToPinIndex(pin);

  if (this.pins[pinIndex].mode !== this.MODES.SERVO) {
    this.pinMode(pin, this.MODES.SERVO);
  }

  this.pins[pinIndex].write(value);

  return this;
};

// By default, these parts are not supported
// by the fs based implementation. This will
// soon be deprecated.
[
  "pulseIn",
  "pulseOut",
  "queryPinState",
  "_sendOneWireRequest",
  "_sendOneWireSearch",
  "sendI2CWriteRequest",
  "sendI2CReadRequest",
  "sendI2CConfig",
  "sendOneWireWriteAndRead",
  "sendOneWireDelay",
  "sendOneWireDelay",
  "sendOneWireReset",
  "sendOneWireRead",
  "sendOneWireSearch",
  "sendOneWireAlarmsSearch",
  "sendOneWireConfig",
  "stepperConfig",
  "stepperStep"
].forEach(function(method) {
  Galileo.prototype[method] = function() {
    throw method + " is not yet implemented.";
  };
});

if (mraa) {

  // Bus determination inferred via
  // https://github.com/intel-iot-devkit/mraa/commit/995565b2e1c3489810c66953e30ca07a83718c51
  // https://github.com/intel-iot-devkit/mraa/commit/dd89adc47b75e3d0f237d07884e0c69faecc6d9c
  //
  // 0 => 0
  // 1 => 0
  // 2 => 6
  //
  var bus = [0, 0, 6][mraa.getPlatformType()];

  // Map to Board.prototype.sendI2CConfig
  Galileo.prototype.i2cConfig = function(delay) {
    var state = priv.get(this);
    // Initialize the I2C interface if none currently exists
    if (!state.i2c) {
      state.i2c = new mraa.I2c(bus);
    }
  };

  // Map to Board.prototype.sendI2CWriteRequest
  Galileo.prototype.i2cWrite = function(address, cmdRegOrData, inBytes) {
    /**
     * cmdRegOrData:
     * [... arbitrary bytes]
     *
     * or
     *
     * cmdRegOrData, inBytes:
     * command [, ...]
     *
     */
    var state = priv.get(this);
    var buffer;

    this.i2cConfig();

    // If i2cWrite was used for an i2cWriteReg call...
    if (arguments.length === 3 &&
        !Array.isArray(cmdRegOrData) &&
        !Array.isArray(inBytes)) {

      return this.i2cWriteReg(address, cmdRegOrData, inBytes);
    }

    // Fix arguments if called with Firmata.js API
    if (arguments.length === 2) {
      if (Array.isArray(cmdRegOrData)) {
        inBytes = cmdRegOrData.slice();
        cmdRegOrData = inBytes.shift();
      } else {
        inBytes = [];
      }
    }

    buffer = new Buffer([cmdRegOrData].concat(inBytes));

    // Only write if bytes provided
    if (buffer.length) {
      // Experimental:
      //
      //  - Throttling writes by making them async
      //
      // i2cQueue.push({
      //   address: address,
      //   buffer: buffer
      // });

      state.i2c.address(address);
      state.i2c.write(buffer.toString("ascii"));

      // Certain devices may not allow a batch value write.
      // for (var i = 0; i < buffer.length; i++) {
      //   state.i2c.write(buffer.toString("ascii", i, i + 1));
      // }
    }

    return this;
  };

  Galileo.prototype.i2cWriteReg = function(address, register, value) {
    var state = priv.get(this);

    this.i2cConfig();

    state.i2c.address(address);
    state.i2c.writeReg(register, value);

    return this;
  };

  // Map to Board.prototype.sendI2CReadRequest
  Galileo.prototype.i2cRead = function(address, cmdOrBytes, bytesToRead, callback) {
    var state = priv.get(this);

    this.i2cConfig();

    // Fix arguments if called with Firmata.js API
    if (arguments.length === 3 &&
        typeof cmdOrBytes === "number" &&
        typeof bytesToRead === "function") {
      callback = bytesToRead;
      bytesToRead = cmdOrBytes;
      cmdOrBytes = null;
    }

    callback = typeof callback === "function" ? callback : function() {};

    if (cmdOrBytes) {
      this.i2cWrite(address, cmdOrBytes);
    }

    this.once("I2C-reply-" + address, callback);

    process.nextTick(function() {
      state.i2c.address(address);

      var data = new Buffer(state.i2c.read(bytesToRead));
      var values = [];

      for (var i = 0; i < bytesToRead; i++) {
        values.push(data.readUInt8(i));
      }

      this.emit("I2C-reply-" + address, values);
    }.bind(this));

    return this;
  };

  // Necessary for Firmata.js compatibility.
  Galileo.prototype.sendI2CWriteRequest = Galileo.prototype.i2cWrite;
  Galileo.prototype.sendI2CReadRequest = Galileo.prototype.i2cRead;
  Galileo.prototype.sendI2CConfig = Galileo.prototype.i2cConfig;
}

if (IS_TEST_ENV) {
  Galileo.__io = io;
  Galileo.__reporting = reporting;
}

module.exports = Galileo;
// http://wiki.ros.org/IntelGalileo/IntelGalileoPin
<|endoftext|>


<commit_before>/**
 * Convert a chain into a text representation in the DOT language, in order to
 * visualize a chain or group of chains as a static execution graph.
 *
 * (c) 2013, Greg Malysa <gmalysa@stanford.edu>
 * Permission to use granted under the terms of the MIT License. See LICENSE for details.
 */

var helpers = require('./helpers');

// Use the same trick as patterns to hook this into the main lib while getting a reference to it
module.exports = function(fl) {
	fl.gen_dot = function(chain) {
		return _dot_inner(chain, true, {})[0].join('\n');
	}

	function _dot_inner(elem, make_digraph, names) {
		var builder = [];
		var name = hash_name_get(elem, names);

		// Only push graphs/clusters for chains
		if (elem instanceof fl.ChainBase) {
			if (make_digraph)
				builder.push('digraph '+name+' {');
			else
				builder.push('subgraph cluster_'+name+' {');
			builder.push('label = '+name+';');

			// Get the graph content for this chain, then add it
			var nest = get_chain_content(elem, names);
			Array.prototype.push.apply(builder, nest[0]);
			var chain_entry = nest[1];
			var chain_exit = nest[2];
			builder.push('}');
			return [builder, chain_entry, chain_exit];
		}
		else {
			// Just a normal function, return it in our results
			return [[], name, name];
		}

	}

	function get_chain_content(elem, names) {
		if (elem instanceof fl.Branch) {
			return handle_branch(elem, names);
		}
		else if (elem instanceof fl.ParallelChain) {
		}
		else if (elem instanceof fl.LoopChain) {
		}
		else if (elem instanceof fl.Chain) {
			return handle_chain(elem, names);
		}
		else {
			// A chain type we don't handle yet...shouldn't happen hopefully
		}
	}

	/**
	 * Handle a branch-type chain, which includes fancy decision labels and
	 * formatting
	 * @param elem The branch element
	 * @param names The hash table of names we've seen
	 * @return Standard triple of [strings, first element, last element]
	 */
	function handle_branch(elem, names) {
		var cond = _dot_inner(elem.cond.fn, false, names);
		var if_true = _dot_inner(elem.if_true.fn, false, names);
		var if_false = _dot_inner(elem.if_false.fn, false, names);
		var terminator = hash_name_get({name : 'branch_end'}, names);
		var builder = [];

		Array.prototype.push.apply(builder, cond[0]);
		Array.prototype.push.apply(builder, if_true[0]);
		Array.prototype.push.apply(builder, if_false[0]);
		builder.push(cond[2]+' -> '+if_true[1]+' [label = true];');
		builder.push(cond[2]+' -> '+if_false[1]+' [label = false];');
		builder.push(if_true[2]+' -> '+terminator+';');
		builder.push(if_false[2]+' -> '+terminator+';');
		return [builder, cond[1], terminator];
	}

	/**
	 * Handle a serial chain of function, which just pushes each node with an
	 * edge to the next node in the chain
	 * @param elem The chain to pull elements from
	 * @param names The hash table of names that we've seen
	 * @return Standard triple of [strings, first element, last element]
	 */
	function handle_chain(elem, names) {
		// Make sure the chain does something
		if (elem.fns.length === 0) {
			return [[], '__empty__', '__empty__'];
		}

		var builder = [];
		var node1 = _dot_inner(elem.fns[0], false, names);
		var first = node1;
		var node2;

		for (var i = 1; i < elem.fns.length; ++i) {
			Array.prototype.push.apply(builder, node1[0]);
			node2 = _dot_inner(elem.fns[i].fn, false, names);
			builder.push(node1[2]+' -> '+node2[1]+';');
			node1 = node2;
		}

		Array.prototype.push.apply(builder, node2[0]);
		return [builder, first[1], node2[2]];
	}

	/**
	 * Retrieve the proper name to use in the graph, for a given element. If it doesn't
	 * exist in the hash table given, then it is added, and its allocated name is returned
	 * @param elem The element (chain or function) to look up in the hash table
	 * @param names The hash table to use to resolve and store names
	 * @return Properly formatted name for the given element
	 */
	function hash_name_get(elem, names) {
		var fname;
		if (elem.fn !== undefined)
			fname = format_name(helpers.fname(elem, elem.fn.name));
		else
			fname = format_name(elem.name);
		var list = names[fname];

		// First unique instance of a function gets to use its proper name
		if (list === undefined) {
			names[fname] = [{fn : elem, name : fname, count : 0}];
			return fname;
		}

		// Search the linear chain for our element
		for (var i = 0; i < list.length; ++i) {
			if (list[i].fn === elem) {
				list[i].count += 1;
				return list[i].name+'__'+list[i].count;
			}
		}

		// Not in the list yet, add it with a unique name index
		fname = fname+'_'+list.length;
		list.push({fn : elem, name : fname, count : 0});
		return fname;
	}

	/**
	 * Formats a chain name properly for display by replacing weird characters
	 * from DOT's perspective with underscores
	 * @param name The name to format
	 * @return String the safely formatted name
	 */
	function format_name(name) {
		return name.replace(/[ \(\)]/g, '_');
	}

};
<commit_msg>Added a lot of missing comment blocks<commit_after>/**
 * Convert a chain into a text representation in the DOT language, in order to
 * visualize a chain or group of chains as a static execution graph.
 *
 * (c) 2013, Greg Malysa <gmalysa@stanford.edu>
 * Permission to use granted under the terms of the MIT License. See LICENSE for details.
 */

var helpers = require('./helpers');

// Use the same trick as patterns to hook this into the main lib while getting a reference to it
module.exports = function(fl) {
	/**
	 * Interface function used to generate a description of the chain in the DOT
	 * language
	 * @param chain A flux-link chain object to describe
	 * @return String a string representation of the chain in DOT
	 */
	fl.gen_dot = function(chain) {
		return _dot_inner(chain, true, {})[0].join('\n');
	}

	/**
	 * Inner worker function used to build the actual DOT description
	 * @param elem The element to describe
	 * @param make_digraph Should this node be instantiated as a digraph or subgraph cluster
	 * @param names The hash table of functions and their names that have been seen
	 * @return Standard triple of [strings, first element, last element]
	 */
	function _dot_inner(elem, make_digraph, names) {
		var builder = [];
		var name = hash_name_get(elem, names);

		// Only push graphs/clusters for chains
		if (elem instanceof fl.ChainBase) {
			if (make_digraph)
				builder.push('digraph '+name+' {');
			else
				builder.push('subgraph cluster_'+name+' {');
			builder.push('label = '+name+';');

			// Get the graph content for this chain, then add it
			var nest = get_chain_content(elem, names);
			Array.prototype.push.apply(builder, nest[0]);
			var chain_entry = nest[1];
			var chain_exit = nest[2];
			builder.push('}');
			return [builder, chain_entry, chain_exit];
		}
		else {
			// Just a normal function, return it in our results
			return [[], name, name];
		}

	}

	/**
	 * Breakout function that simply calls the correct handler based on the type
	 * of the element supplied
	 * @param elem A chain-type object to get a dot description for
	 * @param names The hash table of functions and names that we've seen
	 * @return Standard triple of [strings, first element, last element]
	 */
	function get_chain_content(elem, names) {
		if (elem instanceof fl.Branch) {
			return handle_branch(elem, names);
		}
		else if (elem instanceof fl.ParallelChain) {
			return handle_parallel(elem, names);
		}
		else if (elem instanceof fl.LoopChain) {
		}
		else if (elem instanceof fl.Chain) {
			return handle_chain(elem, names);
		}
		else {
			// A chain type we don't handle yet...shouldn't happen hopefully
		}
	}

	/**
	 * Handle a branch-type chain, which includes fancy decision labels and
	 * formatting
	 * @param elem The branch element
	 * @param names The hash table of names we've seen
	 * @return Standard triple of [strings, first element, last element]
	 */
	function handle_branch(elem, names) {
		var cond = _dot_inner(elem.cond.fn, false, names);
		var if_true = _dot_inner(elem.if_true.fn, false, names);
		var if_false = _dot_inner(elem.if_false.fn, false, names);
		var terminator = hash_name_get({name : 'branch_end'}, names);
		var builder = [];

		Array.prototype.push.apply(builder, cond[0]);
		Array.prototype.push.apply(builder, if_true[0]);
		Array.prototype.push.apply(builder, if_false[0]);
		builder.push(cond[2]+' -> '+if_true[1]+' [label = true];');
		builder.push(cond[2]+' -> '+if_false[1]+' [label = false];');
		builder.push(if_true[2]+' -> '+terminator+';');
		builder.push(if_false[2]+' -> '+terminator+';');
		return [builder, cond[1], terminator];
	}

	/**
	 * Handle a parallel chain, which inserts head and footer nodes around
	 * the functions in the middle
	 * @param elem The parallel chain to describe in DOT
	 * @param names The hash table of functions and their names
	 * @return Standard triple of [strings, first element, last element]
	 */
	function handle_parallel(elem, names) {
	}

	/**
	 * Handle a serial chain of function, which just pushes each node with an
	 * edge to the next node in the chain
	 * @param elem The chain to pull elements from
	 * @param names The hash table of names that we've seen
	 * @return Standard triple of [strings, first element, last element]
	 */
	function handle_chain(elem, names) {
		// Make sure the chain does something
		if (elem.fns.length === 0) {
			return [[], '__empty__', '__empty__'];
		}

		var builder = [];
		var node1 = _dot_inner(elem.fns[0], false, names);
		var first = node1;
		var node2;

		for (var i = 1; i < elem.fns.length; ++i) {
			Array.prototype.push.apply(builder, node1[0]);
			node2 = _dot_inner(elem.fns[i].fn, false, names);
			builder.push(node1[2]+' -> '+node2[1]+';');
			node1 = node2;
		}

		Array.prototype.push.apply(builder, node2[0]);
		return [builder, first[1], node2[2]];
	}

	/**
	 * Retrieve the proper name to use in the graph, for a given element. If it doesn't
	 * exist in the hash table given, then it is added, and its allocated name is returned
	 * @param elem The element (chain or function) to look up in the hash table
	 * @param names The hash table to use to resolve and store names
	 * @return Properly formatted name for the given element
	 */
	function hash_name_get(elem, names) {
		var fname;
		if (elem.fn !== undefined)
			fname = format_name(helpers.fname(elem, elem.fn.name));
		else
			fname = format_name(elem.name);
		var list = names[fname];

		// First unique instance of a function gets to use its proper name
		if (list === undefined) {
			names[fname] = [{fn : elem, name : fname, count : 0}];
			return fname;
		}

		// Search the linear chain for our element
		for (var i = 0; i < list.length; ++i) {
			if (list[i].fn === elem) {
				list[i].count += 1;
				return list[i].name+'__'+list[i].count;
			}
		}

		// Not in the list yet, add it with a unique name index
		fname = fname+'_'+list.length;
		list.push({fn : elem, name : fname, count : 0});
		return fname;
	}

	/**
	 * Formats a chain name properly for display by replacing weird characters
	 * from DOT's perspective with underscores
	 * @param name The name to format
	 * @return String the safely formatted name
	 */
	function format_name(name) {
		return name.replace(/[ \(\)]/g, '_');
	}

};
<|endoftext|>


<commit_before>var general = {
	/**
	 * Linear interpolation between two values
	 * @param value1
	 * @param value2
	 * @param ratio
	 * @returns {*}
	 */
	lerp : function lerp(value1, value2, ratio) {
		"use strict";

		return ((value2 - value1) * this.limit(ratio, 0, 1)) + value1;
	},
	/**
	 * Limit a value to given bounds (aka clamp)
	 * @param value
	 * @param min
	 * @param max
	 * @returns {*}
	 */
	limit : function limit(value, min, max) {
		"use strict";

		if(min > max) {
			var bmin = max;
			max = min;
			min = bmin;
		}

		return this.max(this.min(value, max), min);
	},
	scale : function scale(value, min, max, dstMin, dstMax) {
		"use strict";

		value = (max === min ? 0 : (this.limit(value, min, max) - min) / (max - min));

		return value * (dstMax - dstMin) + dstMin;
	},
	sign1 : function sign1(value) {
		"use strict";

		if(this.isNaN(value)) {
			return NaN;
		} else if(value > 0 || this.isPositiveZero(value) || value === Infinity) {
			return 1;
		} else {
			return -1;
		}
	},
	copySign : function copySign(value, sign) {
		"use strict";

		return this.abs(value) * this.sign1(sign);
	},
	smoothstep : function smoothstep(min, max, value) {
		"use strict";

		value = this.scale(value, min, max, 0, 1);

		return value * value * (3 - 2 * value);
	},
	smootherstep : function smootherstep(min, max, value) {
		"use strict";

		value = this.scale(value, min, max, 0, 1);

		return value * value * value * (value * (value * 6 - 15) + 10);
	},
	step : function(step, value) {
		"use strict";

		if(isNaN(step) || isNaN(value)) {
			return NaN;
		}

		return (value >= step ? 1 : 0);
	},
	factorial : (function() {
		"use strict";

		var factorialCache = { 0 : 1, 1 : 1, 2 : 2, 3 : 6 };

		var factorialFunc = function factorialFunc(value) {
			if(factorialCache[value]) {
				return factorialCache[value];
			} else {
				return (factorialCache[value] = factorialFunc(value - 1) * value);
			}
		};

		return function factorial(value) {
			if (isNaN(value) || value < 0 || value !== this.floor(value)) { //messy isInteger() and positive
				return NaN;
			}

			return factorialFunc(value);
		};
	}())
};

module.exports = general;<commit_msg>implement toPrecision<commit_after>var general = {
	/**
	 * Linear interpolation between two values
	 * @param value1
	 * @param value2
	 * @param ratio
	 * @returns {*}
	 */
	lerp : function lerp(value1, value2, ratio) {
		"use strict";

		return ((value2 - value1) * this.limit(ratio, 0, 1)) + value1;
	},
	/**
	 * Limit a value to given bounds (aka clamp)
	 * @param value
	 * @param min
	 * @param max
	 * @returns {*}
	 */
	limit : function limit(value, min, max) {
		"use strict";

		if(min > max) {
			var bmin = max;
			max = min;
			min = bmin;
		}

		return this.max(this.min(value, max), min);
	},
	scale : function scale(value, min, max, dstMin, dstMax) {
		"use strict";

		value = (max === min ? 0 : (this.limit(value, min, max) - min) / (max - min));

		return value * (dstMax - dstMin) + dstMin;
	},
	sign1 : function sign1(value) {
		"use strict";

		if(this.isNaN(value)) {
			return NaN;
		} else if(value > 0 || this.isPositiveZero(value) || value === Infinity) {
			return 1;
		} else {
			return -1;
		}
	},
	copySign : function copySign(value, sign) {
		"use strict";

		return this.abs(value) * this.sign1(sign);
	},
	smoothstep : function smoothstep(min, max, value) {
		"use strict";

		value = this.scale(value, min, max, 0, 1);

		return value * value * (3 - 2 * value);
	},
	smootherstep : function smootherstep(min, max, value) {
		"use strict";

		value = this.scale(value, min, max, 0, 1);

		return value * value * value * (value * (value * 6 - 15) + 10);
	},
	step : function(step, value) {
		"use strict";

		if(isNaN(step) || isNaN(value)) {
			return NaN;
		}

		return (value >= step ? 1 : 0);
	},
	factorial : (function() {
		"use strict";

		var factorialCache = { 0 : 1, 1 : 1, 2 : 2, 3 : 6 };

		var factorialFunc = function factorialFunc(value) {
			if(factorialCache[value]) {
				return factorialCache[value];
			} else {
				return (factorialCache[value] = factorialFunc(value - 1) * value);
			}
		};

		return function factorial(value) {
			if (isNaN(value) || value < 0 || value !== this.floor(value)) { //messy isInteger() and positive
				return NaN;
			}

			return factorialFunc(value);
		};
	}()),
	toPrecision : function(value, precision) {
		"use strict";

		return +Number(value).toPrecision(precision);
	}
};

module.exports = general;
<|endoftext|>


<commit_before>'use strict';

var get = require('lodash.get');
var plurals = require('./plurals');

module.exports = Gettext;

/**
 * Creates and returns a new Gettext instance.
 *
 * @constructor
 * @param  {Object}  options        A set of options
 * @param  {Boolean} options.debug  Whether to output debug info into the
 *                                  console.
 * @return {Object}  A Gettext instance
 */
function Gettext(options) {
    options = options || {};

    this.catalogs = {};
    this.locale = null;
    this.domain = 'messages';

    this.listeners = [];

    // Set debug flag
    if ('debug' in options) {
        this.debug = options.debug === true;
    }
    else if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV) {
        this.debug = process.env.NODE_ENV !== 'production';
    }
    else {
        this.debug = false;
    }
}

/**
 * Adds an event listener.
 *
 * @param  {String}   eventName  An event name
 * @param  {Function} callback   An event handler function
 */
Gettext.prototype.on = function(eventName, callback) {
    this.listeners.push({
        eventName: eventName,
        callback: callback
    });
};

/**
 * Removes an event listener.
 *
 * @param  {String}   eventName  An event name
 * @param  {Function} callback   A previously registered event handler function
 */
Gettext.prototype.off = function(eventName, callback) {
    this.listeners = this.listeners.filter(function(listener) {
        return (
            listener.eventName === eventName &&
            listener.callback === callback
        ) === false;
    });
};

/**
 * Emits an event to all registered event listener.
 *
 * @param  {String} eventName  An event name
 * @param  {any}    eventData  Data to pass to event listeners
 */
Gettext.prototype.emit = function(eventName, eventData) {
    for (var i = 0; i < this.listeners.length; i++) {
        var listener = this.listeners[i];
        if (listener.eventName === eventName) {
            listener.callback(eventData);
        }
    }
};

/**
 * Logs a warning to the console if debug mode is enabled.
 *
 * @ignore
 * @param  {String} message  A warning message
 */
Gettext.prototype.warn = function(message) {
    if (this.debug) {
        console.warn(message);
    }

    this.emit('error', message);
};

/**
 * Stores a set of translations in the set of gettext
 * catalogs.
 *
 * @example
 *     gt.addTranslations('sv-SE', 'messages', translationsObject)
 *
 * @param {String} locale        A locale string
 * @param {String} domain        A domain name
 * @param {Object} translations  An object of gettext-parser JSON shape
 */
Gettext.prototype.addTranslations = function(locale, domain, translations) {
    if (!this.catalogs[locale]) {
        this.catalogs[locale] = {};
    }

    this.catalogs[locale][domain] = translations;
};

/**
 * Sets the locale to get translated messages for.
 *
 * @example
 *     gt.setLocale('sv-SE')
 *
 * @param {String} locale  A locale
 */
Gettext.prototype.setLocale = function(locale) {
    if (!locale) {
        this.warn('You called setLocale() with an empty value, which makes little sense.');
        return;
    }

    if (!this.catalogs[locale]) {
        this.warn('You called setLocale() with "' + locale + '", but no translations for that locale has been added.');
        return;
    }

    this.locale = locale;
};

/**
 * Sets the default gettext domain.
 *
 * @example
 *     gt.setTextDomain('domainname')
 *
 * @param {String} domain  A gettext domain name
 */
Gettext.prototype.setTextDomain = function(domain) {
    if (!domain) {
        this.warn('You called setTextDomain() with an empty `domain` value, which is not allowed.');
        return;
    }

    this.domain = domain;
};

/**
 * Translates a string using the default textdomain
 *
 * @example
 *     gt.gettext('Some text')
 *
 * @param  {String} msgid  String to be translated
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.gettext = function(msgid) {
    return this.dnpgettext(this.domain, '', msgid);
};

/**
 * Translates a string using a specific domain
 *
 * @example
 *     gt.dgettext('domainname', 'Some text')
 *
 * @param  {String} domain  A gettext domain name
 * @param  {String} msgid   String to be translated
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.dgettext = function(domain, msgid) {
    return this.dnpgettext(domain, '', msgid);
};

/**
 * Translates a plural string using the default textdomain
 *
 * @example
 *     gt.ngettext('One thing', 'Many things', numberOfThings)
 *
 * @param  {String} msgid        String to be translated when count is not plural
 * @param  {String} msgidPlural  String to be translated when count is plural
 * @param  {Number} count        Number count for the plural
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.ngettext = function(msgid, msgidPlural, count) {
    return this.dnpgettext(this.domain, '', msgid, msgidPlural, count);
};

/**
 * Translates a plural string using a specific textdomain
 *
 * @example
 *     gt.dngettext('domainname', 'One thing', 'Many things', numberOfThings)
 *
 * @param  {String} domain       A gettext domain name
 * @param  {String} msgid        String to be translated when count is not plural
 * @param  {String} msgidPlural  String to be translated when count is plural
 * @param  {Number} count        Number count for the plural
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.dngettext = function(domain, msgid, msgidPlural, count) {
    return this.dnpgettext(domain, '', msgid, msgidPlural, count);
};

/**
 * Translates a string from a specific context using the default textdomain
 *
 * @example
 *    gt.pgettext('sports', 'Back')
 *
 * @param  {String} msgctxt  Translation context
 * @param  {String} msgid    String to be translated
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.pgettext = function(msgctxt, msgid) {
    return this.dnpgettext(this.domain, msgctxt, msgid);
};

/**
 * Translates a string from a specific context using s specific textdomain
 *
 * @example
 *     gt.dpgettext('domainname', 'sports', 'Back')
 *
 * @param  {String} domain   A gettext domain name
 * @param  {String} msgctxt  Translation context
 * @param  {String} msgid    String to be translated
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.dpgettext = function(domain, msgctxt, msgid) {
    return this.dnpgettext(domain, msgctxt, msgid);
};

/**
 * Translates a plural string from a specific context using the default textdomain
 *
 * @example
 *     gt.npgettext('sports', 'Back', '%d backs', numberOfBacks)
 *
 * @param  {String} msgctxt      Translation context
 * @param  {String} msgid        String to be translated when count is not plural
 * @param  {String} msgidPlural  String to be translated when count is plural
 * @param  {Number} count        Number count for the plural
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.npgettext = function(msgctxt, msgid, msgidPlural, count) {
    return this.dnpgettext(this.domain, msgctxt, msgid, msgidPlural, count);
};

/**
 * Translates a plural string from a specifi context using a specific textdomain
 *
 * @example
 *     gt.dnpgettext('domainname', 'sports', 'Back', '%d backs', numberOfBacks)
 *
 * @param  {String} domain       A gettext domain name
 * @param  {String} msgctxt      Translation context
 * @param  {String} msgid        String to be translated
 * @param  {String} msgidPlural  If no translation was found, return this on count!=1
 * @param  {Number} count        Number count for the plural
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.dnpgettext = function(domain, msgctxt, msgid, msgidPlural, count) {
    var defaultTranslation = msgid;
    var translation;
    var index;

    msgctxt = msgctxt || '';

    if (!isNaN(count) && count !== 1) {
        defaultTranslation = msgidPlural || msgid;
    }

    if (!this.locale) {
        this.warn('You need to set a locale using setLocale(locale) before getting translated messages.');
        return defaultTranslation;
    }

    translation = this._getTranslation(domain, msgctxt, msgid);

    if (translation) {
        if (typeof count === 'number') {
            var pluralsFunc = plurals[Gettext.getLanguageCode(this.locale)].pluralsFunc;
            index = pluralsFunc(count);
            if (typeof index === 'boolean') {
                index = index ? 1 : 0;
            }
        } else {
            index = 0;
        }

        return translation.msgstr[index] || defaultTranslation;
    }
    else {
        this.warn('No translation was found for msgid "' + msgid + '" in msgctxt "' + msgctxt + '" and domain "' + domain + '"');
    }

    return defaultTranslation;
};

/**
 * Retrieves comments object for a translation. The comments object
 * has the shape `{ translator, extracted, reference, flag, previous }`.
 *
 * @example
 *     const comment = gt.getComment('domainname', 'sports', 'Backs')
 *
 * @param  {String} domain   A gettext domain name
 * @param  {String} msgctxt  Translation context
 * @param  {String} msgid    String to be translated
 * @return {Object} Comments object or false if not found
 */
Gettext.prototype.getComment = function(domain, msgctxt, msgid) {
    var translation;

    translation = this._getTranslation(domain, msgctxt, msgid);
    if (translation) {
        return translation.comments || {};
    }

    return {};
};

/**
 * Retrieves translation object from the domain and context
 *
 * @private
 * @param  {String} domain   A gettext domain name
 * @param  {String} msgctxt  Translation context
 * @param  {String} msgid    String to be translated
 * @return {Object} Translation object or false if not found
 */
Gettext.prototype._getTranslation = function(domain, msgctxt, msgid) {
    msgctxt = msgctxt || '';

    return get(this.catalogs, [this.locale, domain, 'translations', msgctxt, msgid]);
};

/**
 * Returns the language code part of a locale
 *
 * @example
 *     Gettext.getLanguageCode('sv-SE')
 *     // -> "sv"
 *
 * @param {String} locale  A case-insensitive locale string
 * @returns {String} A language code
 */
Gettext.getLanguageCode = function(locale) {
    return locale.split(/[\-_]/)[0].toLowerCase();
};

/**
 * This function will be removed in the final 2.0.0 release.
 *
 * @deprecated
 */
Gettext.prototype.addTextdomain = function() {

    // TODO(alexanderwallin): Add instructions for file i/o
    console.error('addTextdomain() is deprecated.\n\n' +
        '* To add translations, use addTranslations()\n' +
        '* To set the default domain, use setTextDomain()\n' +
        '\n' +
        'To read more about the migration from node-gettext v1 to v2, ' +
        'see https://github.com/alexanderwallin/node-gettext/#migrating-from-1x-to-2x');
};

/**
 * This function will be removed in the final 2.0.0 release.
 *
 * @deprecated
 */
Gettext.prototype.textdomain = function() {
    console.error('textdomain() is deprecated.\n\n' +
        '* To set the current locale, use setLocale()\n' +
        '* To set the default domain, use setTextDomain()\n' +
        '\n' +
        'To read more about the migration from node-gettext v1 to v2, ' +
        'see https://github.com/alexanderwallin/node-gettext/#migrating-from-1x-to-2x');
};
<commit_msg>Update jsDoc for Gettext constructor.<commit_after>'use strict';

var get = require('lodash.get');
var plurals = require('./plurals');

module.exports = Gettext;

/**
 * Creates and returns a new Gettext instance.
 *
 * @constructor
 * @param  {Object}  [options]        A set of options
 * @param  {Boolean} options.debug  Whether to output debug info into the
 *                                  console.
 * @return {Object}  A Gettext instance
 */
function Gettext(options) {
    options = options || {};

    this.catalogs = {};
    this.locale = null;
    this.domain = 'messages';

    this.listeners = [];

    // Set debug flag
    if ('debug' in options) {
        this.debug = options.debug === true;
    }
    else if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV) {
        this.debug = process.env.NODE_ENV !== 'production';
    }
    else {
        this.debug = false;
    }
}

/**
 * Adds an event listener.
 *
 * @param  {String}   eventName  An event name
 * @param  {Function} callback   An event handler function
 */
Gettext.prototype.on = function(eventName, callback) {
    this.listeners.push({
        eventName: eventName,
        callback: callback
    });
};

/**
 * Removes an event listener.
 *
 * @param  {String}   eventName  An event name
 * @param  {Function} callback   A previously registered event handler function
 */
Gettext.prototype.off = function(eventName, callback) {
    this.listeners = this.listeners.filter(function(listener) {
        return (
            listener.eventName === eventName &&
            listener.callback === callback
        ) === false;
    });
};

/**
 * Emits an event to all registered event listener.
 *
 * @param  {String} eventName  An event name
 * @param  {any}    eventData  Data to pass to event listeners
 */
Gettext.prototype.emit = function(eventName, eventData) {
    for (var i = 0; i < this.listeners.length; i++) {
        var listener = this.listeners[i];
        if (listener.eventName === eventName) {
            listener.callback(eventData);
        }
    }
};

/**
 * Logs a warning to the console if debug mode is enabled.
 *
 * @ignore
 * @param  {String} message  A warning message
 */
Gettext.prototype.warn = function(message) {
    if (this.debug) {
        console.warn(message);
    }

    this.emit('error', message);
};

/**
 * Stores a set of translations in the set of gettext
 * catalogs.
 *
 * @example
 *     gt.addTranslations('sv-SE', 'messages', translationsObject)
 *
 * @param {String} locale        A locale string
 * @param {String} domain        A domain name
 * @param {Object} translations  An object of gettext-parser JSON shape
 */
Gettext.prototype.addTranslations = function(locale, domain, translations) {
    if (!this.catalogs[locale]) {
        this.catalogs[locale] = {};
    }

    this.catalogs[locale][domain] = translations;
};

/**
 * Sets the locale to get translated messages for.
 *
 * @example
 *     gt.setLocale('sv-SE')
 *
 * @param {String} locale  A locale
 */
Gettext.prototype.setLocale = function(locale) {
    if (!locale) {
        this.warn('You called setLocale() with an empty value, which makes little sense.');
        return;
    }

    if (!this.catalogs[locale]) {
        this.warn('You called setLocale() with "' + locale + '", but no translations for that locale has been added.');
        return;
    }

    this.locale = locale;
};

/**
 * Sets the default gettext domain.
 *
 * @example
 *     gt.setTextDomain('domainname')
 *
 * @param {String} domain  A gettext domain name
 */
Gettext.prototype.setTextDomain = function(domain) {
    if (!domain) {
        this.warn('You called setTextDomain() with an empty `domain` value, which is not allowed.');
        return;
    }

    this.domain = domain;
};

/**
 * Translates a string using the default textdomain
 *
 * @example
 *     gt.gettext('Some text')
 *
 * @param  {String} msgid  String to be translated
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.gettext = function(msgid) {
    return this.dnpgettext(this.domain, '', msgid);
};

/**
 * Translates a string using a specific domain
 *
 * @example
 *     gt.dgettext('domainname', 'Some text')
 *
 * @param  {String} domain  A gettext domain name
 * @param  {String} msgid   String to be translated
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.dgettext = function(domain, msgid) {
    return this.dnpgettext(domain, '', msgid);
};

/**
 * Translates a plural string using the default textdomain
 *
 * @example
 *     gt.ngettext('One thing', 'Many things', numberOfThings)
 *
 * @param  {String} msgid        String to be translated when count is not plural
 * @param  {String} msgidPlural  String to be translated when count is plural
 * @param  {Number} count        Number count for the plural
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.ngettext = function(msgid, msgidPlural, count) {
    return this.dnpgettext(this.domain, '', msgid, msgidPlural, count);
};

/**
 * Translates a plural string using a specific textdomain
 *
 * @example
 *     gt.dngettext('domainname', 'One thing', 'Many things', numberOfThings)
 *
 * @param  {String} domain       A gettext domain name
 * @param  {String} msgid        String to be translated when count is not plural
 * @param  {String} msgidPlural  String to be translated when count is plural
 * @param  {Number} count        Number count for the plural
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.dngettext = function(domain, msgid, msgidPlural, count) {
    return this.dnpgettext(domain, '', msgid, msgidPlural, count);
};

/**
 * Translates a string from a specific context using the default textdomain
 *
 * @example
 *    gt.pgettext('sports', 'Back')
 *
 * @param  {String} msgctxt  Translation context
 * @param  {String} msgid    String to be translated
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.pgettext = function(msgctxt, msgid) {
    return this.dnpgettext(this.domain, msgctxt, msgid);
};

/**
 * Translates a string from a specific context using s specific textdomain
 *
 * @example
 *     gt.dpgettext('domainname', 'sports', 'Back')
 *
 * @param  {String} domain   A gettext domain name
 * @param  {String} msgctxt  Translation context
 * @param  {String} msgid    String to be translated
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.dpgettext = function(domain, msgctxt, msgid) {
    return this.dnpgettext(domain, msgctxt, msgid);
};

/**
 * Translates a plural string from a specific context using the default textdomain
 *
 * @example
 *     gt.npgettext('sports', 'Back', '%d backs', numberOfBacks)
 *
 * @param  {String} msgctxt      Translation context
 * @param  {String} msgid        String to be translated when count is not plural
 * @param  {String} msgidPlural  String to be translated when count is plural
 * @param  {Number} count        Number count for the plural
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.npgettext = function(msgctxt, msgid, msgidPlural, count) {
    return this.dnpgettext(this.domain, msgctxt, msgid, msgidPlural, count);
};

/**
 * Translates a plural string from a specifi context using a specific textdomain
 *
 * @example
 *     gt.dnpgettext('domainname', 'sports', 'Back', '%d backs', numberOfBacks)
 *
 * @param  {String} domain       A gettext domain name
 * @param  {String} msgctxt      Translation context
 * @param  {String} msgid        String to be translated
 * @param  {String} msgidPlural  If no translation was found, return this on count!=1
 * @param  {Number} count        Number count for the plural
 * @return {String} Translation or the original string if no translation was found
 */
Gettext.prototype.dnpgettext = function(domain, msgctxt, msgid, msgidPlural, count) {
    var defaultTranslation = msgid;
    var translation;
    var index;

    msgctxt = msgctxt || '';

    if (!isNaN(count) && count !== 1) {
        defaultTranslation = msgidPlural || msgid;
    }

    if (!this.locale) {
        this.warn('You need to set a locale using setLocale(locale) before getting translated messages.');
        return defaultTranslation;
    }

    translation = this._getTranslation(domain, msgctxt, msgid);

    if (translation) {
        if (typeof count === 'number') {
            var pluralsFunc = plurals[Gettext.getLanguageCode(this.locale)].pluralsFunc;
            index = pluralsFunc(count);
            if (typeof index === 'boolean') {
                index = index ? 1 : 0;
            }
        } else {
            index = 0;
        }

        return translation.msgstr[index] || defaultTranslation;
    }
    else {
        this.warn('No translation was found for msgid "' + msgid + '" in msgctxt "' + msgctxt + '" and domain "' + domain + '"');
    }

    return defaultTranslation;
};

/**
 * Retrieves comments object for a translation. The comments object
 * has the shape `{ translator, extracted, reference, flag, previous }`.
 *
 * @example
 *     const comment = gt.getComment('domainname', 'sports', 'Backs')
 *
 * @param  {String} domain   A gettext domain name
 * @param  {String} msgctxt  Translation context
 * @param  {String} msgid    String to be translated
 * @return {Object} Comments object or false if not found
 */
Gettext.prototype.getComment = function(domain, msgctxt, msgid) {
    var translation;

    translation = this._getTranslation(domain, msgctxt, msgid);
    if (translation) {
        return translation.comments || {};
    }

    return {};
};

/**
 * Retrieves translation object from the domain and context
 *
 * @private
 * @param  {String} domain   A gettext domain name
 * @param  {String} msgctxt  Translation context
 * @param  {String} msgid    String to be translated
 * @return {Object} Translation object or false if not found
 */
Gettext.prototype._getTranslation = function(domain, msgctxt, msgid) {
    msgctxt = msgctxt || '';

    return get(this.catalogs, [this.locale, domain, 'translations', msgctxt, msgid]);
};

/**
 * Returns the language code part of a locale
 *
 * @example
 *     Gettext.getLanguageCode('sv-SE')
 *     // -> "sv"
 *
 * @param {String} locale  A case-insensitive locale string
 * @returns {String} A language code
 */
Gettext.getLanguageCode = function(locale) {
    return locale.split(/[\-_]/)[0].toLowerCase();
};

/**
 * This function will be removed in the final 2.0.0 release.
 *
 * @deprecated
 */
Gettext.prototype.addTextdomain = function() {

    // TODO(alexanderwallin): Add instructions for file i/o
    console.error('addTextdomain() is deprecated.\n\n' +
        '* To add translations, use addTranslations()\n' +
        '* To set the default domain, use setTextDomain()\n' +
        '\n' +
        'To read more about the migration from node-gettext v1 to v2, ' +
        'see https://github.com/alexanderwallin/node-gettext/#migrating-from-1x-to-2x');
};

/**
 * This function will be removed in the final 2.0.0 release.
 *
 * @deprecated
 */
Gettext.prototype.textdomain = function() {
    console.error('textdomain() is deprecated.\n\n' +
        '* To set the current locale, use setLocale()\n' +
        '* To set the default domain, use setTextDomain()\n' +
        '\n' +
        'To read more about the migration from node-gettext v1 to v2, ' +
        'see https://github.com/alexanderwallin/node-gettext/#migrating-from-1x-to-2x');
};
<|endoftext|>


<commit_before>/**

  globals.js

  There are multiple globals we choose not to pass between functions:

  • config       Configuration Object
  • db           Database (via LowDB)
  • framework    Underlying framework (Express.js)
  • fs           File System (via MemoryFS)
  • log          Convenience method to log info
  • logger       Logging via Winston
  • pipelines    Compilation pipelines (pipemaker.js)
  • ports        Ports used by application
  • router       Framework's router

  By intentionally poluting the global namespace, we allow users to share
  functionality between their application instance and the core runner.

**/

var express = require("express"),
    events = require("events"),
    uuid = require("uuid"),
    Utils = require("./utils");

module.exports = function(options) {

  var log;

  // Global namespace/event emitter
  global.shipp = new events();

  // Create secret (for cache warming)
  global.shipp.uuid = uuid.v4();

  // Expose logging and handle exceptions (as well as convenience function)
  global.shipp.logger = require("./logging").setup();
  global.shipp.log = global.shipp.logger.info;

  // Configuration
  global.shipp.log("Reading configuration");
  global.shipp.config = require("./config")();

  // Cache
  global.shipp.cache = require("./cache");

  // Requests
  global.shipp.request = Utils.internalRequest;

  // List of key assets
  global.shipp.assets = {};

  // Database: note that we use require if local file and desires otherwise. desires creates
  // npm-debug errors when used with faulty repositories, which can tend to happen with local dev
  global.shipp.log("Attaching database");
  if (global.shipp.config.adapters.database) {
    pkg = global.shipp.config.adapters.database;
    if ("." === pkg[0] || "/" === pkg[0])
      global.shipp.db = require(Utils.makePathAbsolute(pkg));
    else
      global.shipp.db = require("desires")(pkg);
  } else {
    dataStore = require("./data-store");
    // Iteration isn't data lossy since each call appends to global database
    (global.shipp.config.data || []).forEach(function(store) {
      global.shipp.db = dataStore(store);
    });
  }

  // Exposes framework/router (for ease-of-use in user-defined routes). While "framework" is the
  // correct usage, we maintain "express", backwards compatibility
  global.shipp.framework = global.shipp.express = require("express");
  global.shipp.router = global.shipp.framework.Router;

  // File system
  global.shipp.log("Attaching file system");
  global.shipp.fs = new (require("memory-fs"))();

  // Server
  if (options.liveRefresh) {
    // Add internal port (proxy of BrowserSync)
    global.shipp.ports = { server: 27182 };
  } else {
    global.shipp.ports = { server: global.shipp.config.port || process.env.PORT || 3000 };
  }

  // Pipelines
  global.shipp.log("Attaching pipelines");
  global.shipp.pipelines = require("superloader").pipelines;
  global.shipp.pipelines.dir = process.cwd();

  // Add pipelines in config
  for (var ext in global.shipp.config.pipelines)
    global.shipp.pipelines.addPipeline(ext, global.shipp.config.pipelines[ext]);

};
<commit_msg>Attach UUID to routers (for easier removal)<commit_after>/**

  globals.js

  There are multiple globals we choose not to pass between functions:

  • config       Configuration Object
  • db           Database (via LowDB)
  • framework    Underlying framework (Express.js)
  • fs           File System (via MemoryFS)
  • log          Convenience method to log info
  • logger       Logging via Winston
  • pipelines    Compilation pipelines (pipemaker.js)
  • ports        Ports used by application
  • router       Framework's router

  By intentionally poluting the global namespace, we allow users to share
  functionality between their application instance and the core runner.

**/

var express = require("express"),
    events = require("events"),
    uuid = require("uuid"),
    Utils = require("./utils");

module.exports = function(options) {

  var log;

  // Global namespace/event emitter
  global.shipp = new events();

  // Create secret (for cache warming)
  global.shipp.uuid = uuid.v4();

  // Expose logging and handle exceptions (as well as convenience function)
  global.shipp.logger = require("./logging").setup();
  global.shipp.log = global.shipp.logger.info;

  // Configuration
  global.shipp.log("Reading configuration");
  global.shipp.config = require("./config")();

  // Cache
  global.shipp.cache = require("./cache");

  // Requests
  global.shipp.request = Utils.internalRequest;

  // List of key assets
  global.shipp.assets = {};

  // Database: note that we use require if local file and desires otherwise. desires creates
  // npm-debug errors when used with faulty repositories, which can tend to happen with local dev
  global.shipp.log("Attaching database");
  if (global.shipp.config.adapters.database) {
    pkg = global.shipp.config.adapters.database;
    if ("." === pkg[0] || "/" === pkg[0])
      global.shipp.db = require(Utils.makePathAbsolute(pkg));
    else
      global.shipp.db = require("desires")(pkg);
  } else {
    dataStore = require("./data-store");
    // Iteration isn't data lossy since each call appends to global database
    (global.shipp.config.data || []).forEach(function(store) {
      global.shipp.db = dataStore(store);
    });
  }

  // Exposes framework. While "framework" is the
  // correct usage, we maintain "express", backwards compatibility
  global.shipp.framework = global.shipp.express = require("express");

  // Exposes router and assigns uuid (for ease-of-use in user-defined routes)
  global.shipp.router = function() {
    var router = global.shipp.framework.Router.apply(null, Array.prototype.slice.apply(arguments));
    router.uuid = uuid.v4();
    return router;
  }

  // File system
  global.shipp.log("Attaching file system");
  global.shipp.fs = new (require("memory-fs"))();

  // Server
  if (options.liveRefresh) {
    // Add internal port (proxy of BrowserSync)
    global.shipp.ports = { server: 27182 };
  } else {
    global.shipp.ports = { server: global.shipp.config.port || process.env.PORT || 3000 };
  }

  // Pipelines
  global.shipp.log("Attaching pipelines");
  global.shipp.pipelines = require("superloader").pipelines;
  global.shipp.pipelines.dir = process.cwd();

  // Add pipelines in config
  for (var ext in global.shipp.config.pipelines)
    global.shipp.pipelines.addPipeline(ext, global.shipp.config.pipelines[ext]);

};
<|endoftext|>