<commit_before>#! /usr/bin/env node

// configuration:

var path_to_boxee = "/Applications/Boxee.app";

// udp socket stuff
var dgram = require("dgram");
var server = dgram.createSocket("udp4");

// shell execution stuff
var util = require('util'),
    exec = require('child_process').exec,
    child;

server.on("message", function (msg, rinfo) {
  //console.log("server got: " + msg + " from " + rinfo.address + ":" + rinfo.port);

	// if it receives a discover packet, then let's try to launch boxee
	if ( msg.toString('utf8').match(/cmd="discover"/) ) {
		console.log("We got a discover packet!!! Launching Boxee at " + path_to_boxee);

		// launch boxee!
		child = exec('open "' + path_to_boxee + '"',
		  function (error, stdout, stderr) {
		    if (error !== null) {
		      console.log('exec error: ' + error);
		    }
		});

	} else {
		console.log("NOT a discover packet...");
	}

});

server.on("listening", function () {
  var address = server.address();
  console.log("server listening " + address.address + ":" + address.port);
});

server.bind(2562);
<commit_msg>rewrote it a bit to support killing and checking if things are running. added console log output for everything.<commit_after>#! /usr/bin/env node

// configuration:

var path_to_boxee = "/Applications/TextEdit.app";

// udp socket stuff
var dgram = require("dgram");
var server = dgram.createSocket("udp4");

// shell execution stuff
var util = require('util'),
    exec = require('child_process').exec,
    child;

// if boxee isn't running, listen, otherwise, wait until boxee stops running, then listen.
function listen () {
	console.log('ready to listen... check if boxee is running...');

	is_boxee_running(
		function() {
			console.log("Boxee is running... let's wait 2.5 seconds and try again...");

			setTimeout(listen, 2500); // wait 2.5 seconds and try to listen again
		},
		function() {
			console.log("Boxee is not running... let's start listening.");

			try {
				server.bind(2562);
			} catch (err) {
				console.log("Boxee MUST be running!");
				child = true;
			}

			setTimeout(listen, 2500); // wait 2.5 seconds and try again (in case boxee exits)
		}
	);

}

// returns true if boxee is running
// checks this by looking at child.pid and hitting it with kill -0
function is_boxee_running (callback_on_true, callback_on_false) {
	if (typeof callback_on_true !== 'function') {
		console.log("callback_on_true not defined.");
		callback_on_true = function() { return false; };
	}

	if (typeof callback_on_false !== 'function') {
		console.log("callback_on_false not defined.");
		callback_on_false = function() { return false; };
	}

	if (!child) {
		console.log("no child process... must not be running.")
		// if there's no child, yet, then it's not running
		callback_on_false();
		return;
	}

	// this happens when we try to listen on the UDP port, but boxee is already running and listening.
	if (child === true) {
		callback_on_true();
		return;
	}

	// send kill -0 to child.pid.
	console.log("Running Kill command");
	var kill_cmd = exec('kill -0 ' + child.pid,
		function (error, stdout, stderr) {
			(error !== null) ? callback_on_true() : callback_on_false();
	});
}

server.on("message", function (msg, rinfo) {
  //console.log("server got: " + msg + " from " + rinfo.address + ":" + rinfo.port);

	// if it receives a discover packet, then let's try to launch boxee
	if ( msg.toString('utf8').match(/cmd="discover"/) ) {
		console.log("We got a discover packet!!! Launching Boxee at " + path_to_boxee);

		// launch boxee!
		child = exec('open "' + path_to_boxee + '"',
		  function (error, stdout, stderr) {
		    if (error !== null) {
		      console.log('exec error: ' + error);
		    }
		});

		server.close();

	} else {
		console.log("NOT a discover packet...");
	}

});

server.on("listening", function () {
  var address = server.address();
  console.log("server listening " + address.address + ":" + address.port);
});

listen();
<|endoftext|>


<commit_before>/* jshint node: true */
/* global require, module */

var EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

var app = new EmberAddon();

app.import('bower_components/jquery-mockjax/jquery.mockjax.js');

module.exports = app.toTree();
<commit_msg>Only include mockjax in development environment.<commit_after>/* jshint node: true */
/* global require, module */

var EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

var app = new EmberAddon();

app.import({ development: 'bower_components/jquery-mockjax/jquery.mockjax.js'});

module.exports = app.toTree();
<|endoftext|>


<commit_before>/**
 * @URL https://github.com/csetea/magpieJS
 * @license MIT
 */
define([ 'magpie/log!magpie/html5/widget/select/m-option'  ], //
function(log) {

	var forEach = Array.prototype.forEach;

	return {
		tag : 'm-option',

		select: function(mSelectEl, mOptionEl, event){

			if (mOptionEl.hasAttribute('disabled')){
				mSelectEl.blur();
			} else if (mOptionEl.hasAttribute('prevent')){
				mSelectEl.preventClose=true;
				// ...
			}else{
				var selected =  !mOptionEl.hasAttribute('selected');
				var multiple = mSelectEl.hasAttribute('multiple');
				var oldSelection = mSelectEl.selection();
				if (!multiple){
					forEach.call(mSelectEl.querySelectorAll('m-option'), function( optionEl ){
						optionEl.removeAttribute('selected');
					});
				}
				if (selected){
					mOptionEl.setAttribute('selected','true');
				}else{
					mOptionEl.removeAttribute('selected');
				}
				mSelectEl.fire('selectionChanged', {
					oldSelection: oldSelection,
					newSelection: mSelectEl.selection(),
					source: mOptionEl,
					selected: selected
				});
			}
		},

		createdCallback : function() {

		},

		attachedCallback: function(){
			if (!this._m_select){
				this._m_select = this.parentNode;
				if (this._m_select && this._m_select.localName != 'm-select'){
					this._m_select = this._m_select.parentNode;
					if (this._m_select &&  this._m_select.localName != 'm-select'){
						this._m_select = this._m_select.parentNode;
						if (this._m_select &&  this._m_select.localName != 'm-select'){
							this._m_select = this._m_select.parentNode;
						}
					}
				}

				var _this=this;
				this._EventListener=function(event){
					if (this.parentNode !==null && this.parentNode.hasAttribute('result') ){
						// do nothing,
						// this is a copy from original m-option element in result holder
//					}else if( this.hasAttribute('prevent')){
//						// prevent
					}else{

						_this.select(_this._m_select, _this ,event);
					}

				};
				this.onclick = this._EventListener;
			}
		}

	};

});
<commit_msg>FireFox bugfix: '_sourceOptionEl' instead of 'event.relatedTarget' <commit_after>/**
 * @URL https://github.com/csetea/magpieJS
 * @license MIT
 */
define([ 'magpie/log!magpie/html5/widget/select/m-option'  ], //
function(log) {

	var forEach = Array.prototype.forEach;

	return {
		tag : 'm-option',

		select: function(mSelectEl, mOptionEl, event){
			if (mOptionEl.hasAttribute('disabled')){
				mSelectEl.blur();
			} else if (mOptionEl.hasAttribute('prevent')){
				// ...
			}else{
				var selected =  !mOptionEl.hasAttribute('selected');
				var multiple = mSelectEl.hasAttribute('multiple');
				var oldSelection = mSelectEl.selection();
				if (!multiple){
					forEach.call(mSelectEl.querySelectorAll('m-option'), function( optionEl ){
						optionEl.removeAttribute('selected');
					});
				}
				if (selected){
					mOptionEl.setAttribute('selected','true');
				}else{
					mOptionEl.removeAttribute('selected');
				}
				mSelectEl.fire('selectionChanged', {
					oldSelection: oldSelection,
					newSelection: mSelectEl.selection(),
					source: mOptionEl,
					selected: selected
				});
			}
		},

		createdCallback : function() {

		},

		attachedCallback: function(){
			if (!this._m_select){
				this._m_select = this.parentNode;
				if (this._m_select && this._m_select.localName != 'm-select'){
					this._m_select = this._m_select.parentNode;
					if (this._m_select &&  this._m_select.localName != 'm-select'){
						this._m_select = this._m_select.parentNode;
						if (this._m_select &&  this._m_select.localName != 'm-select'){
							this._m_select = this._m_select.parentNode;
						}
					}
				}

				var _this=this;
				this._EventListener=function(event){
					if (this.parentNode !==null && this.parentNode.hasAttribute('result') ){
						// do nothing,
						// this is a copy from original m-option element in result holder
					}else{

						_this.select(_this._m_select, _this ,event);
					}
				};
				this.onlcick = this._EventListener;
			}
		}

	};

});
<|endoftext|>


<commit_before>'use strict'

describe('tzAlign', function () {
  var scope, $filter, $compile, $timeout, $sandbox

  beforeEach(module('Timezones', function ($provide) {
    $provide.constant('$timezones.definitions.location', 'base/tz/data')
  }))

  beforeEach(inject(function ($injector, $rootScope, _$filter_, _$compile_, _$timeout_, $rootElement) {
    scope = $rootScope.$new(true)
    $filter = _$filter_
    $compile = _$compile_
    $timeout = _$timeout_

    $sandbox = $rootElement.append(angular.element('

'))
  }))

  var compile = function (markup, timezone, reference) {
    angular.extend(scope, {
      timezone : timezone,
      reference : reference
    })

    var $element = $(markup).appendTo($sandbox)

    $element = $compile($element)(scope)
    scope.$digest()

    return $element
  }

  afterEach(function () {
    $sandbox.remove()
    scope.$destroy()
  })

  var scenarios = [
    {
      timezone : 'America/New_York',
      reference : new Date(Date.parse('1970-01-01T00:00:00+00:00')),
      markup : '{{reference|tzAlign:timezone|date:"yyyy-MM-dd HH:mm:ss Z"}}',
      expected : {
        fullYear : 1969,
        month : 11,
        date : 31,
        hours : 19,
        text : '1969-12-31 19:00:00 -0500'
      }
    },
    {
      timezone : 'America/Los_Angeles',
      reference : new Date(Date.parse('1970-01-01T00:00:00+00:00')),
      markup : '{{reference|tzAlign:timezone|date:"yyyy-MM-dd HH:mm:ss Z"}}',
      expected : {
        fullYear : 1969,
        month : 11,
        date : 31,
        hours : 16,
        text : '1969-12-31 16:00:00 -0800'
      }
    },
    {
      timezone : 'Europe/Berlin',
      reference : new Date(Date.parse('1970-01-01T00:00:00+00:00')),
      markup : '{{reference|tzAlign:timezone|date:"yyyy-MM-dd HH:mm:ss Z"}}',
      expected : {
        fullYear : 1970,
        month : 0,
        date : 1,
        hours : 1,
        text : '1970-01-01 01:00:00 +0100'
      }
    },
    {
      timezone : 'Australia/Sydney',
      reference : new Date(Date.parse('1970-01-01T00:00:00+00:00')),
      markup : '{{reference|tzAlign:timezone|date:"yyyy-MM-dd HH:mm:ss Z"}}',
      expected : {
        fullYear : 1970,
        month : 0,
        date : 1,
        hours : 10,
        text : '1970-01-01 10:00:00 +1000'
      }
    }
  ]

  it('should align dates to expected timezones', function () {
    scenarios.forEach(function (scenario) {
      var timezone = scenario.timezone
        , reference = scenario.reference
        , expected = scenario.expected

      var actual = $filter('tzAlign')(reference, timezone)

      expect(actual.getTimezone()).toEqual(timezone)
      expect(actual.getFullYear()).toEqual(expected.fullYear)
      expect(actual.getMonth()).toEqual(expected.month)
      expect(actual.getDate()).toEqual(expected.date)
      expect(actual.getHours()).toEqual(expected.hours)
    })
  })

  it('should pass the date through unchanged when timezone is undefined', function () {
    scenarios.forEach(function (scenario) {
      var reference = scenario.reference
        , expected = scenario.reference
        , actual

      expect(function () {
        actual = $filter('tzAlign')(reference, undefined)
      }).not.toThrow()

      expect(actual).toBe(expected)
    })
  })

  it('should pass through invalid date values', function () {
    var expected = 'bogus value'
      , actual

    expect(function () {
      actual = $filter('tzAlign')(expected, 'America/New_York')
    }).not.toThrow()

    expect(actual).toBe(expected)
  })

  it('should pass through null date values', function () {
    var actual

    expect(function () {
      actual = $filter('tzAlign')(null, 'America/New_York')
    }).not.toThrow()

    expect(actual).toBeNull()
  })

  it('should pass through undefined date values', function () {
    var actual

    expect(function () {
      actual = $filter('tzAlign')(undefined, 'America/New_York')
    }).not.toThrow()

    expect(actual).toBeUndefined()
  })

  it('should pass through boolean date values', function () {
    var actual

    expect(function () {
      actual = $filter('tzAlign')(true, 'America/New_York')
    }).not.toThrow()

    expect(actual).toBe(true)
  })

  it('should align date objects that are formatted correctly', function () {
    scenarios.forEach(function (scenario) {
      var expected = scenario.expected
        , el = compile(scenario.markup, scenario.timezone, scenario.reference)

      expect(el.text()).toEqual(expected.text)

      scope.timezone = 'America/Chicago'
      scope.$digest()

      expect(el.text()).not.toEqual(expected.text)
    })
  })

  it('should align milliseconds from epoch as numeric literals that are formatted', function () {
    scenarios.forEach(function (scenario) {
      var expected = scenario.expected
        , el = compile(scenario.markup, scenario.timezone, scenario.reference.getTime())

      expect(el.text()).toEqual(expected.text)
    })
  })

  it('should align milliseconds from epoch as string literals that are formatted', function () {
    scenarios.forEach(function (scenario) {
      var expected = scenario.expected
        , el = compile(scenario.markup, scenario.timezone, '' + scenario.reference.getTime())

      expect(el.text()).toEqual(expected.text)
    })
  })
})<commit_msg>Catch more invalid argument types.<commit_after>'use strict'

describe('tzAlign', function () {
  var scope, $filter, $compile, $timeout, $sandbox

  beforeEach(module('Timezones', function ($provide) {
    $provide.constant('$timezones.definitions.location', 'base/tz/data')
  }))

  beforeEach(inject(function ($injector, $rootScope, _$filter_, _$compile_, _$timeout_, $rootElement) {
    scope = $rootScope.$new(true)
    $filter = _$filter_
    $compile = _$compile_
    $timeout = _$timeout_

    $sandbox = $rootElement.append(angular.element('

'))
  }))

  var compile = function (markup, timezone, reference) {
    angular.extend(scope, {
      timezone : timezone,
      reference : reference
    })

    var $element = $(markup).appendTo($sandbox)

    $element = $compile($element)(scope)
    scope.$digest()

    return $element
  }

  afterEach(function () {
    $sandbox.remove()
    scope.$destroy()
  })

  var scenarios = [
    {
      timezone : 'America/New_York',
      reference : new Date(Date.parse('1970-01-01T00:00:00+00:00')),
      markup : '{{reference|tzAlign:timezone|date:"yyyy-MM-dd HH:mm:ss Z"}}',
      expected : {
        fullYear : 1969,
        month : 11,
        date : 31,
        hours : 19,
        text : '1969-12-31 19:00:00 -0500'
      }
    },
    {
      timezone : 'America/Los_Angeles',
      reference : new Date(Date.parse('1970-01-01T00:00:00+00:00')),
      markup : '{{reference|tzAlign:timezone|date:"yyyy-MM-dd HH:mm:ss Z"}}',
      expected : {
        fullYear : 1969,
        month : 11,
        date : 31,
        hours : 16,
        text : '1969-12-31 16:00:00 -0800'
      }
    },
    {
      timezone : 'Europe/Berlin',
      reference : new Date(Date.parse('1970-01-01T00:00:00+00:00')),
      markup : '{{reference|tzAlign:timezone|date:"yyyy-MM-dd HH:mm:ss Z"}}',
      expected : {
        fullYear : 1970,
        month : 0,
        date : 1,
        hours : 1,
        text : '1970-01-01 01:00:00 +0100'
      }
    },
    {
      timezone : 'Australia/Sydney',
      reference : new Date(Date.parse('1970-01-01T00:00:00+00:00')),
      markup : '{{reference|tzAlign:timezone|date:"yyyy-MM-dd HH:mm:ss Z"}}',
      expected : {
        fullYear : 1970,
        month : 0,
        date : 1,
        hours : 10,
        text : '1970-01-01 10:00:00 +1000'
      }
    }
  ]

  it('should align dates to expected timezones', function () {
    scenarios.forEach(function (scenario) {
      var timezone = scenario.timezone
        , reference = scenario.reference
        , expected = scenario.expected

      var actual = $filter('tzAlign')(reference, timezone)

      expect(actual.getTimezone()).toEqual(timezone)
      expect(actual.getFullYear()).toEqual(expected.fullYear)
      expect(actual.getMonth()).toEqual(expected.month)
      expect(actual.getDate()).toEqual(expected.date)
      expect(actual.getHours()).toEqual(expected.hours)
    })
  })

  it('should pass the date through unchanged when timezone is undefined', function () {
    scenarios.forEach(function (scenario) {
      var reference = scenario.reference
        , expected = scenario.reference
        , actual

      expect(function () {
        actual = $filter('tzAlign')(reference, undefined)
      }).not.toThrow()

      expect(actual).toBe(expected)
    })
  })

  it('should pass through invalid date values', function () {
    var expected = 'bogus value'
      , actual

    expect(function () {
      actual = $filter('tzAlign')(expected, 'America/New_York')
    }).not.toThrow()

    expect(actual).toBe(expected)
  })

  it('should pass through null date values', function () {
    var actual

    expect(function () {
      actual = $filter('tzAlign')(null, 'America/New_York')
    }).not.toThrow()

    expect(actual).toBeNull()
  })

  it('should pass through undefined date values', function () {
    var actual

    expect(function () {
      actual = $filter('tzAlign')(undefined, 'America/New_York')
    }).not.toThrow()

    expect(actual).toBeUndefined()
  })

  it('should pass through boolean date values', function () {
    var actual

    expect(function () {
      actual = $filter('tzAlign')(true, 'America/New_York')
    }).not.toThrow()

    expect(actual).toBe(true)
  })

  it('should pass through dates when timezone is null', function () {
    var expected = new Date(1970, 1, 1)
      , actual = $filter('tzAlign')(expected, null)

    expect(actual).toEqual(expected)
  })

  it('should pass through dates when timezone is undefined', function () {
    var expected = new Date(1970, 1, 1)
      , actual = $filter('tzAlign')(expected, undefined)

    expect(actual).toEqual(expected)
  })

  it('should pass through dates when timezone is an object', function () {
    var expected = new Date(1970, 1, 1)
      , actual = $filter('tzAlign')(expected, {})

    expect(actual).toEqual(expected)
  })

  it('should pass through dates when timezone is a number', function () {
    var expected = new Date(1970, 1, 1)
      , actual = $filter('tzAlign')(expected, 1234)

    expect(actual).toEqual(expected)
  })

  it('should pass through dates when timezone is an invalid string', function () {
    var expected = new Date(1970, 1, 1)
      , actual = $filter('tzAlign')(expected, 'bogus')

    expect(actual).toEqual(expected)
  })

  it('should align date objects that are formatted correctly', function () {
    scenarios.forEach(function (scenario) {
      var expected = scenario.expected
        , el = compile(scenario.markup, scenario.timezone, scenario.reference)

      expect(el.text()).toEqual(expected.text)

      scope.timezone = 'America/Chicago'
      scope.$digest()

      expect(el.text()).not.toEqual(expected.text)
    })
  })

  it('should align milliseconds from epoch as numeric literals that are formatted', function () {
    scenarios.forEach(function (scenario) {
      var expected = scenario.expected
        , el = compile(scenario.markup, scenario.timezone, scenario.reference.getTime())

      expect(el.text()).toEqual(expected.text)
    })
  })

  it('should align milliseconds from epoch as string literals that are formatted', function () {
    scenarios.forEach(function (scenario) {
      var expected = scenario.expected
        , el = compile(scenario.markup, scenario.timezone, '' + scenario.reference.getTime())

      expect(el.text()).toEqual(expected.text)
    })
  })
})<|endoftext|>


<commit_before>'use strict';
const _ = require('lodash');
const dd = require('dedent');
const fs = require('fs');
const joi = require('joi');
const semver = require('semver');

const actions = require('@arangodb/actions');
const ArangoError = require('@arangodb').ArangoError;
const errors = require('@arangodb').errors;
const fm = require('@arangodb/foxx/manager');
const fmu = require('@arangodb/foxx/manager-utils');
const createRouter = require('@arangodb/foxx/router');
const reporters = Object.keys(require('@arangodb/mocha').reporters);
const schemas = require('./schemas');

const router = createRouter();
module.context.registerType('multipart/form-data', require('./multipart'));
module.context.use(router);

const legacyErrors = new Map([
  [errors.ERROR_SERVICE_INVALID_NAME.code, errors.ERROR_SERVICE_SOURCE_NOT_FOUND.code],
  [errors.ERROR_SERVICE_INVALID_MOUNT.code, errors.ERROR_INVALID_MOUNTPOINT.code],
  [errors.ERROR_SERVICE_DOWNLOAD_FAILED.code, errors.ERROR_SERVICE_SOURCE_ERROR.code],
  [errors.ERROR_SERVICE_UPLOAD_FAILED.code, errors.ERROR_SERVICE_SOURCE_ERROR.code]
]);

const serviceToJson = (service) => (
  {
    mount: service.mount,
    path: service.basePath,
    name: service.manifest.name,
    version: service.manifest.version,
    development: service.isDevelopment,
    legacy: service.legacy,
    manifest: service.manifest,
    options: _.pick(service.options, ['configuration', 'dependencies'])
  }
);

function isLegacy (service) {
  const range = service.manifest.engines && service.manifest.engines.arangodb;
  return range ? semver.gtr('3.0.0', range) : false;
}

function writeUploadToTempFile (buffer) {
  const filename = fs.getTempFile('foxx-manager', true);
  fs.writeFileSync(filename, buffer);
  return filename;
}

function prepareServiceRequestBody (req, res, next) {
  if (req.body.source instanceof Buffer) {
    req.body.source = writeUploadToTempFile(req.body.source);
  }
  try {
    if (req.body.dependencies) {
      req.body.dependencies = JSON.parse(req.body.dependencies);
    }
    if (req.body.configuration) {
      req.body.configuration = JSON.parse(req.body.configuration);
    }
  } catch (e) {
    throw new ArangoError({
      errorNum: errors.ERROR_SERVICE_OPTIONS_MALFORMED.code,
      errorMessage: dd`
        ${errors.ERROR_SERVICE_OPTIONS_MALFORMED.message}
        Details: ${e.message}
      `
    }, {cause: e});
  }
  next();
}

router.use((req, res, next) => {
  try {
    next();
  } catch (e) {
    if (e.isArangoError) {
      const errorNum = legacyErrors.get(e.errorNum) || e.errorNum;
      const status = actions.arangoErrorToHttpCode(errorNum);
      res.throw(status, e.errorMessage, {errorNum, cause: e});
    }
    throw e;
  }
});

router.get((req, res) => {
  res.json(
    fmu.getStorage().toArray()
    .map((service) => (
      {
        mount: service.mount,
        name: service.manifest.name,
        version: service.manifest.version,
        provides: service.manifest.provides || {},
        development: service.isDevelopment,
        legacy: isLegacy(service)
      }
    ))
  );
})
.response(200, joi.array().items(schemas.shortInfo).required(), `Array of service descriptions.`)
.summary(`List installed services`)
.description(dd`
  Fetches a list of services installed in the current database.
`);

router.post(prepareServiceRequestBody, (req, res) => {
  const mount = req.queryParams.mount;
  fm.install(req.body.source, mount, _.omit(req.queryParams, ['mount', 'development']));
  if (req.body.configuration) {
    fm.setConfiguration(mount, {configuration: req.body.configuration, replace: true});
  }
  if (req.body.dependencies) {
    fm.setDependencies(mount, {dependencies: req.body.dependencies, replace: true});
  }
  if (req.queryParams.development) {
    fm.development(mount);
  }
  const service = fm.lookupService(mount);
  res.json(serviceToJson(service));
})
.body(schemas.service, ['multipart/form-data', 'application/json'], `Service to be installed.`)
.queryParam('mount', schemas.mount, `Mount path the service should be installed at.`)
.queryParam('development', schemas.flag.default(false), `Enable development mode.`)
.queryParam('setup', schemas.flag.default(true), `Run the service's setup script.`)
.queryParam('legacy', schemas.flag.default(false), `Service should be installed in 2.8 legacy compatibility mode.`)
.response(201, schemas.fullInfo, `Description of the installed service.`)
.summary(`Install new service`)
.description(dd`
  Installs the given new service at the given mount path.

  The service source can be specified as either an absolute local file path,
  a fully qualified URL reachable from the database server,
  or as a binary zip file using multipart form upload.
`);

const instanceRouter = createRouter();
instanceRouter.use((req, res, next) => {
  const mount = req.queryParams.mount;
  try {
    req.service = fm.lookupService(mount);
  } catch (e) {
    res.throw(400, `No service installed at mount path "${mount}".`, e);
  }
  next();
})
.queryParam('mount', schemas.mount, `Mount path of the installed service.`);
router.use(instanceRouter);

const serviceRouter = createRouter();
instanceRouter.use('/service', serviceRouter);

serviceRouter.get((req, res) => {
  res.json(serviceToJson(req.service));
})
.response(200, schemas.fullInfo, `Description of the service.`)
.summary(`Service description`)
.description(dd`
  Fetches detailed information for the service at the given mount path.
`);

serviceRouter.patch(prepareServiceRequestBody, (req, res) => {
  const mount = req.queryParams.mount;
  fm.upgrade(req.body.source, mount, _.omit(req.queryParams, ['mount']));
  if (req.body.configuration) {
    fm.setConfiguration(mount, {configuration: req.body.configuration, replace: false});
  }
  if (req.body.dependencies) {
    fm.setDependencies(mount, {dependencies: req.body.dependencies, replace: false});
  }
  const service = fm.lookupService(mount);
  res.json(serviceToJson(service));
})
.body(schemas.service, ['multipart/form-data', 'application/json'], `Service to be installed.`)
.queryParam('teardown', schemas.flag.default(false), `Run the old service's teardown script.`)
.queryParam('setup', schemas.flag.default(true), `Run the new service's setup script.`)
.queryParam('legacy', schemas.flag.default(false), `Service should be installed in 2.8 legacy compatibility mode.`)
.response(200, schemas.fullInfo, `Description of the new service.`)
.summary(`Upgrade service`)
.description(dd`
  Installs the given new service on top of the service currently installed at the given mount path.
  This is only recommended for switching between different versions of the same service.

  The service source can be specified as either an absolute local file path,
  a fully qualified URL reachable from the database server,
  or as a binary zip file using multipart form upload.
`);

serviceRouter.put(prepareServiceRequestBody, (req, res) => {
  const mount = req.queryParams.mount;
  fm.replace(req.body.source, mount, _.omit(req.queryParams, ['mount']));
  if (req.body.configuration) {
    fm.setConfiguration(mount, {configuration: req.body.configuration, replace: true});
  }
  if (req.body.dependencies) {
    fm.setDependencies(mount, {dependencies: req.body.dependencies, replace: true});
  }
  const service = fm.lookupService(mount);
  res.json(serviceToJson(service));
})
.body(schemas.service, ['multipart/form-data', 'application/json'], `Service to be installed.`)
.queryParam('teardown', schemas.flag.default(true), `Run the old service's teardown script.`)
.queryParam('setup', schemas.flag.default(true), `Run the new service's setup script.`)
.queryParam('legacy', schemas.flag.default(false), `Service should be installed in 2.8 legacy compatibility mode.`)
.response(200, schemas.fullInfo, `Description of the new service.`)
.summary(`Replace service`)
.description(dd`
  Removes the service at the given mount path from the database and file system.
  Then installs the given new service at the same mount path.

  The service source can be specified as either an absolute local file path,
  a fully qualified URL reachable from the database server,
  or as a binary zip file using multipart form upload.
`);

serviceRouter.delete((req, res) => {
  fm.uninstall(
    req.queryParams.mount,
    _.omit(req.queryParams, ['mount'])
  );
  res.status(204);
})
.queryParam('teardown', schemas.flag.default(true), `Run the service's teardown script.`)
.response(204, null, `Empty response.`)
.summary(`Uninstall service`)
.description(dd`
  Removes the service at the given mount path from the database and file system.
`);

const configRouter = createRouter();
instanceRouter.use('/configuration', configRouter)
.response(200, schemas.configs, `Configuration options of the service.`);

configRouter.get((req, res) => {
  res.json(fm.configuration(req.service.mount));
})
.summary(`Get configuration options`)
.description(dd`
  Fetches the current configuration for the service at the given mount path.
`);

configRouter.patch((req, res) => {
  const warnings = fm.setConfiguration(req.service.mount, {
    configuration: req.body,
    replace: false
  });
  const values = fm.configuration(req.service.mount, {simple: true});
  res.json({values, warnings});
})
.body(joi.object().required(), `Object mapping configuration names to values.`)
.summary(`Update configuration options`)
.description(dd`
  Replaces the given service's configuration.
  Any omitted options will be ignored.
`);

configRouter.put((req, res) => {
  const warnings = fm.setConfiguration(req.service.mount, {
    configuration: req.body,
    replace: true
  });
  const values = fm.configuration(req.service.mount, {simple: true});
  res.json({values, warnings});
})
.body(joi.object().required(), `Object mapping configuration names to values.`)
.summary(`Replace configuration options`)
.description(dd`
  Replaces the given service's configuration completely.
  Any omitted options will be reset to their default values or marked as unconfigured.
`);

const depsRouter = createRouter();
instanceRouter.use('/dependencies', depsRouter)
.response(200, schemas.deps, `Dependency options of the service.`);

depsRouter.get((req, res) => {
  res.json(fm.dependencies(req.service.mount));
})
.summary(`Get dependency options`)
.description(dd`
  Fetches the current dependencies for service at the given mount path.
`);

depsRouter.patch((req, res) => {
  const warnings = fm.setDependencies(req.service.mount, {
    dependencies: req.body,
    replace: true
  });
  const values = fm.dependencies(req.service.mount, {simple: true});
  res.json({values, warnings});
})
.body(joi.object().required(), `Object mapping dependency aliases to mount paths.`)
.summary(`Update dependency options`)
.description(dd`
  Replaces the given service's dependencies.
  Any omitted dependencies will be ignored.
`);

depsRouter.put((req, res) => {
  const warnings = fm.setDependencies(req.service.mount, {
    dependencies: req.body,
    replace: true
  });
  const values = fm.dependencies(req.service.mount, {simple: true});
  res.json({values, warnings});
})
.body(joi.object().required(), `Object mapping dependency aliases to mount paths.`)
.summary(`Replace dependency options`)
.description(dd`
  Replaces the given service's dependencies completely.
  Any omitted dependencies will be disabled.
`);

const devRouter = createRouter();
instanceRouter.use('/development', devRouter)
.response(200, schemas.fullInfo, `Description of the service.`);

devRouter.post((req, res) => {
  const service = fm.development(req.service.mount);
  res.json(serviceToJson(service));
})
.summary(`Enable development mode`)
.description(dd`
  Puts the service into development mode.
  The service will be re-installed from the filesystem for each request.
`);

devRouter.delete((req, res) => {
  const service = fm.production(req.service.mount);
  res.json(serviceToJson(service));
})
.summary(`Disable development mode`)
.description(dd`
  Puts the service at the given mount path into production mode.
  Changes to the service's code will no longer be reflected automatically.
`);

const scriptsRouter = createRouter();
instanceRouter.use('/scripts', scriptsRouter);

scriptsRouter.get((req, res) => {
  res.json(req.service.getScripts());
})
.response(200, joi.array().items(joi.object({
  name: joi.string().required().description(`Script name`),
  title: joi.string().required().description(`Human-readable script name`)
}).required()).required(), `List of scripts available on the service.`)
.summary(`List service scripts`)
.description(dd`
  Fetches a list of the scripts defined by the service.
`);

scriptsRouter.post('/:name', (req, res) => {
  const service = req.service;
  const scriptName = req.pathParams.name;
  res.json(fm.runScript(scriptName, service.mount, req.body) || null);
})
.body(joi.any(), `Optional script arguments.`)
.pathParam('name', joi.string().required(), `Name of the script to run`)
.response(200, joi.any().default(null), `Script result if any.`)
.summary(`Run service script`)
.description(dd`
  Runs the given script for the service at the given mount path.
  Returns the exports of the script, if any.
`);

instanceRouter.post('/download', (req, res) => {
  const service = req.service;
  const dir = fs.join(fs.makeAbsolute(service.root), service.path);
  const zipPath = fmu.zipDirectory(dir);
  const name = service.mount.replace(/^\/|\/$/g, '').replace(/\//g, '_');
  res.download(zipPath, `${name}.zip`);
})
.response(200, ['application/zip'], `Zip bundle of the service.`)
.summary(`Download service bundle`)
.description(dd`
  Creates and downloads a zip bundle of the service directory.
`);

instanceRouter.post('/tests', (req, res) => {
  const service = req.service;
  const reporter = req.queryParams.reporter || null;
  res.json(fm.runTests(service.mount, {reporter}));
})
.queryParam('reporter', joi.only(...reporters).optional(), `Test reporter to use`)
.response(200, joi.object(), `Test results.`)
.summary(`Run service tests`)
.description(dd`
  Runs the tests for the service at the given mount path and returns the results.
`);

instanceRouter.get('/readme', (req, res) => {
  const service = req.service;
  res.send(fm.readme(service.mount));
})
.response(200, ['text/plain'], `Raw README contents.`)
.summary(`Service README`)
.description(dd`
  Fetches the service's README or README.md file's contents if any.
`);
<commit_msg>Add swagger route to Foxx API<commit_after>'use strict';
const _ = require('lodash');
const dd = require('dedent');
const fs = require('fs');
const joi = require('joi');
const semver = require('semver');

const actions = require('@arangodb/actions');
const ArangoError = require('@arangodb').ArangoError;
const errors = require('@arangodb').errors;
const swaggerJson = require('@arangodb/foxx/legacy/swagger').swaggerJson;
const fm = require('@arangodb/foxx/manager');
const fmu = require('@arangodb/foxx/manager-utils');
const createRouter = require('@arangodb/foxx/router');
const reporters = Object.keys(require('@arangodb/mocha').reporters);
const schemas = require('./schemas');

const router = createRouter();
module.context.registerType('multipart/form-data', require('./multipart'));
module.context.use(router);

const legacyErrors = new Map([
  [errors.ERROR_SERVICE_INVALID_NAME.code, errors.ERROR_SERVICE_SOURCE_NOT_FOUND.code],
  [errors.ERROR_SERVICE_INVALID_MOUNT.code, errors.ERROR_INVALID_MOUNTPOINT.code],
  [errors.ERROR_SERVICE_DOWNLOAD_FAILED.code, errors.ERROR_SERVICE_SOURCE_ERROR.code],
  [errors.ERROR_SERVICE_UPLOAD_FAILED.code, errors.ERROR_SERVICE_SOURCE_ERROR.code]
]);

const serviceToJson = (service) => (
  {
    mount: service.mount,
    path: service.basePath,
    name: service.manifest.name,
    version: service.manifest.version,
    development: service.isDevelopment,
    legacy: service.legacy,
    manifest: service.manifest,
    options: _.pick(service.options, ['configuration', 'dependencies'])
  }
);

function isLegacy (service) {
  const range = service.manifest.engines && service.manifest.engines.arangodb;
  return range ? semver.gtr('3.0.0', range) : false;
}

function writeUploadToTempFile (buffer) {
  const filename = fs.getTempFile('foxx-manager', true);
  fs.writeFileSync(filename, buffer);
  return filename;
}

function prepareServiceRequestBody (req, res, next) {
  if (req.body.source instanceof Buffer) {
    req.body.source = writeUploadToTempFile(req.body.source);
  }
  try {
    if (req.body.dependencies) {
      req.body.dependencies = JSON.parse(req.body.dependencies);
    }
    if (req.body.configuration) {
      req.body.configuration = JSON.parse(req.body.configuration);
    }
  } catch (e) {
    throw new ArangoError({
      errorNum: errors.ERROR_SERVICE_OPTIONS_MALFORMED.code,
      errorMessage: dd`
        ${errors.ERROR_SERVICE_OPTIONS_MALFORMED.message}
        Details: ${e.message}
      `
    }, {cause: e});
  }
  next();
}

router.use((req, res, next) => {
  try {
    next();
  } catch (e) {
    if (e.isArangoError) {
      const errorNum = legacyErrors.get(e.errorNum) || e.errorNum;
      const status = actions.arangoErrorToHttpCode(errorNum);
      res.throw(status, e.errorMessage, {errorNum, cause: e});
    }
    throw e;
  }
});

router.get((req, res) => {
  res.json(
    fmu.getStorage().toArray()
    .map((service) => (
      {
        mount: service.mount,
        name: service.manifest.name,
        version: service.manifest.version,
        provides: service.manifest.provides || {},
        development: service.isDevelopment,
        legacy: isLegacy(service)
      }
    ))
  );
})
.response(200, joi.array().items(schemas.shortInfo).required(), `Array of service descriptions.`)
.summary(`List installed services`)
.description(dd`
  Fetches a list of services installed in the current database.
`);

router.post(prepareServiceRequestBody, (req, res) => {
  const mount = req.queryParams.mount;
  fm.install(req.body.source, mount, _.omit(req.queryParams, ['mount', 'development']));
  if (req.body.configuration) {
    fm.setConfiguration(mount, {configuration: req.body.configuration, replace: true});
  }
  if (req.body.dependencies) {
    fm.setDependencies(mount, {dependencies: req.body.dependencies, replace: true});
  }
  if (req.queryParams.development) {
    fm.development(mount);
  }
  const service = fm.lookupService(mount);
  res.json(serviceToJson(service));
})
.body(schemas.service, ['multipart/form-data', 'application/json'], `Service to be installed.`)
.queryParam('mount', schemas.mount, `Mount path the service should be installed at.`)
.queryParam('development', schemas.flag.default(false), `Enable development mode.`)
.queryParam('setup', schemas.flag.default(true), `Run the service's setup script.`)
.queryParam('legacy', schemas.flag.default(false), `Service should be installed in 2.8 legacy compatibility mode.`)
.response(201, schemas.fullInfo, `Description of the installed service.`)
.summary(`Install new service`)
.description(dd`
  Installs the given new service at the given mount path.

  The service source can be specified as either an absolute local file path,
  a fully qualified URL reachable from the database server,
  or as a binary zip file using multipart form upload.
`);

const instanceRouter = createRouter();
instanceRouter.use((req, res, next) => {
  const mount = req.queryParams.mount;
  try {
    req.service = fm.lookupService(mount);
  } catch (e) {
    res.throw(400, `No service installed at mount path "${mount}".`, e);
  }
  next();
})
.queryParam('mount', schemas.mount, `Mount path of the installed service.`);
router.use(instanceRouter);

const serviceRouter = createRouter();
instanceRouter.use('/service', serviceRouter);

serviceRouter.get((req, res) => {
  res.json(serviceToJson(req.service));
})
.response(200, schemas.fullInfo, `Description of the service.`)
.summary(`Service description`)
.description(dd`
  Fetches detailed information for the service at the given mount path.
`);

serviceRouter.patch(prepareServiceRequestBody, (req, res) => {
  const mount = req.queryParams.mount;
  fm.upgrade(req.body.source, mount, _.omit(req.queryParams, ['mount']));
  if (req.body.configuration) {
    fm.setConfiguration(mount, {configuration: req.body.configuration, replace: false});
  }
  if (req.body.dependencies) {
    fm.setDependencies(mount, {dependencies: req.body.dependencies, replace: false});
  }
  const service = fm.lookupService(mount);
  res.json(serviceToJson(service));
})
.body(schemas.service, ['multipart/form-data', 'application/json'], `Service to be installed.`)
.queryParam('teardown', schemas.flag.default(false), `Run the old service's teardown script.`)
.queryParam('setup', schemas.flag.default(true), `Run the new service's setup script.`)
.queryParam('legacy', schemas.flag.default(false), `Service should be installed in 2.8 legacy compatibility mode.`)
.response(200, schemas.fullInfo, `Description of the new service.`)
.summary(`Upgrade service`)
.description(dd`
  Installs the given new service on top of the service currently installed at the given mount path.
  This is only recommended for switching between different versions of the same service.

  The service source can be specified as either an absolute local file path,
  a fully qualified URL reachable from the database server,
  or as a binary zip file using multipart form upload.
`);

serviceRouter.put(prepareServiceRequestBody, (req, res) => {
  const mount = req.queryParams.mount;
  fm.replace(req.body.source, mount, _.omit(req.queryParams, ['mount']));
  if (req.body.configuration) {
    fm.setConfiguration(mount, {configuration: req.body.configuration, replace: true});
  }
  if (req.body.dependencies) {
    fm.setDependencies(mount, {dependencies: req.body.dependencies, replace: true});
  }
  const service = fm.lookupService(mount);
  res.json(serviceToJson(service));
})
.body(schemas.service, ['multipart/form-data', 'application/json'], `Service to be installed.`)
.queryParam('teardown', schemas.flag.default(true), `Run the old service's teardown script.`)
.queryParam('setup', schemas.flag.default(true), `Run the new service's setup script.`)
.queryParam('legacy', schemas.flag.default(false), `Service should be installed in 2.8 legacy compatibility mode.`)
.response(200, schemas.fullInfo, `Description of the new service.`)
.summary(`Replace service`)
.description(dd`
  Removes the service at the given mount path from the database and file system.
  Then installs the given new service at the same mount path.

  The service source can be specified as either an absolute local file path,
  a fully qualified URL reachable from the database server,
  or as a binary zip file using multipart form upload.
`);

serviceRouter.delete((req, res) => {
  fm.uninstall(
    req.queryParams.mount,
    _.omit(req.queryParams, ['mount'])
  );
  res.status(204);
})
.queryParam('teardown', schemas.flag.default(true), `Run the service's teardown script.`)
.response(204, null, `Empty response.`)
.summary(`Uninstall service`)
.description(dd`
  Removes the service at the given mount path from the database and file system.
`);

const configRouter = createRouter();
instanceRouter.use('/configuration', configRouter)
.response(200, schemas.configs, `Configuration options of the service.`);

configRouter.get((req, res) => {
  res.json(fm.configuration(req.service.mount));
})
.summary(`Get configuration options`)
.description(dd`
  Fetches the current configuration for the service at the given mount path.
`);

configRouter.patch((req, res) => {
  const warnings = fm.setConfiguration(req.service.mount, {
    configuration: req.body,
    replace: false
  });
  const values = fm.configuration(req.service.mount, {simple: true});
  res.json({values, warnings});
})
.body(joi.object().required(), `Object mapping configuration names to values.`)
.summary(`Update configuration options`)
.description(dd`
  Replaces the given service's configuration.
  Any omitted options will be ignored.
`);

configRouter.put((req, res) => {
  const warnings = fm.setConfiguration(req.service.mount, {
    configuration: req.body,
    replace: true
  });
  const values = fm.configuration(req.service.mount, {simple: true});
  res.json({values, warnings});
})
.body(joi.object().required(), `Object mapping configuration names to values.`)
.summary(`Replace configuration options`)
.description(dd`
  Replaces the given service's configuration completely.
  Any omitted options will be reset to their default values or marked as unconfigured.
`);

const depsRouter = createRouter();
instanceRouter.use('/dependencies', depsRouter)
.response(200, schemas.deps, `Dependency options of the service.`);

depsRouter.get((req, res) => {
  res.json(fm.dependencies(req.service.mount));
})
.summary(`Get dependency options`)
.description(dd`
  Fetches the current dependencies for service at the given mount path.
`);

depsRouter.patch((req, res) => {
  const warnings = fm.setDependencies(req.service.mount, {
    dependencies: req.body,
    replace: true
  });
  const values = fm.dependencies(req.service.mount, {simple: true});
  res.json({values, warnings});
})
.body(joi.object().required(), `Object mapping dependency aliases to mount paths.`)
.summary(`Update dependency options`)
.description(dd`
  Replaces the given service's dependencies.
  Any omitted dependencies will be ignored.
`);

depsRouter.put((req, res) => {
  const warnings = fm.setDependencies(req.service.mount, {
    dependencies: req.body,
    replace: true
  });
  const values = fm.dependencies(req.service.mount, {simple: true});
  res.json({values, warnings});
})
.body(joi.object().required(), `Object mapping dependency aliases to mount paths.`)
.summary(`Replace dependency options`)
.description(dd`
  Replaces the given service's dependencies completely.
  Any omitted dependencies will be disabled.
`);

const devRouter = createRouter();
instanceRouter.use('/development', devRouter)
.response(200, schemas.fullInfo, `Description of the service.`);

devRouter.post((req, res) => {
  const service = fm.development(req.service.mount);
  res.json(serviceToJson(service));
})
.summary(`Enable development mode`)
.description(dd`
  Puts the service into development mode.
  The service will be re-installed from the filesystem for each request.
`);

devRouter.delete((req, res) => {
  const service = fm.production(req.service.mount);
  res.json(serviceToJson(service));
})
.summary(`Disable development mode`)
.description(dd`
  Puts the service at the given mount path into production mode.
  Changes to the service's code will no longer be reflected automatically.
`);

const scriptsRouter = createRouter();
instanceRouter.use('/scripts', scriptsRouter);

scriptsRouter.get((req, res) => {
  res.json(req.service.getScripts());
})
.response(200, joi.array().items(joi.object({
  name: joi.string().required().description(`Script name`),
  title: joi.string().required().description(`Human-readable script name`)
}).required()).required(), `List of scripts available on the service.`)
.summary(`List service scripts`)
.description(dd`
  Fetches a list of the scripts defined by the service.
`);

scriptsRouter.post('/:name', (req, res) => {
  const service = req.service;
  const scriptName = req.pathParams.name;
  res.json(fm.runScript(scriptName, service.mount, req.body) || null);
})
.body(joi.any(), `Optional script arguments.`)
.pathParam('name', joi.string().required(), `Name of the script to run`)
.response(200, joi.any().default(null), `Script result if any.`)
.summary(`Run service script`)
.description(dd`
  Runs the given script for the service at the given mount path.
  Returns the exports of the script, if any.
`);

instanceRouter.post('/download', (req, res) => {
  const service = req.service;
  const dir = fs.join(fs.makeAbsolute(service.root), service.path);
  const zipPath = fmu.zipDirectory(dir);
  const name = service.mount.replace(/^\/|\/$/g, '').replace(/\//g, '_');
  res.download(zipPath, `${name}.zip`);
})
.response(200, ['application/zip'], `Zip bundle of the service.`)
.summary(`Download service bundle`)
.description(dd`
  Creates and downloads a zip bundle of the service directory.
`);

instanceRouter.post('/tests', (req, res) => {
  const service = req.service;
  const reporter = req.queryParams.reporter || null;
  res.json(fm.runTests(service.mount, {reporter}));
})
.queryParam('reporter', joi.only(...reporters).optional(), `Test reporter to use`)
.response(200, joi.object(), `Test results.`)
.summary(`Run service tests`)
.description(dd`
  Runs the tests for the service at the given mount path and returns the results.
`);

instanceRouter.get('/readme', (req, res) => {
  const service = req.service;
  res.send(fm.readme(service.mount));
})
.response(200, ['text/plain'], `Raw README contents.`)
.summary(`Service README`)
.description(dd`
  Fetches the service's README or README.md file's contents if any.
`);

instanceRouter.get('/swagger', (req, res) => {
  swaggerJson(req, res, {
    mount: req.service.mount
  });
})
.response(200, joi.object(), `Service Swagger description.`)
.summary(`Swagger description`)
.description(dd`
  Fetches the Swagger API description for the service at the given mount path.
`);
<|endoftext|>


<commit_before>// Copyright 2014-2015, University of Colorado Boulder

/**
 * A round toggle button that switches the value of a property that can take on valueUp or valueDown.  It
 * sticks in the down position when pressed, popping back up when pressed
 * again.
 *
 * @author John Blanco
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var RoundButtonView = require( 'SUN/buttons/RoundButtonView' );
  var StickyToggleButtonInteractionStateProperty = require( 'SUN/buttons/StickyToggleButtonInteractionStateProperty' );
  var StickyToggleButtonModel = require( 'SUN/buttons/StickyToggleButtonModel' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );

  /**
   * @param {Object} valueUp value when the toggle is in the 'up' position
   * @param {Object} valueDown value when the toggle is in the 'down' position
   * @param {Property} property axon property that can be either valueUp or valueDown.
   * @param {Object} [options]
   * @constructor
   */
  function RoundStickyToggleButton( valueUp, valueDown, property, options ) {

    Tandem.validateOptions( options ); // The tandem is required when brand==='phet-io'

    var buttonModel = new StickyToggleButtonModel( valueUp, valueDown, property );
    RoundButtonView.call( this, buttonModel, new StickyToggleButtonInteractionStateProperty( buttonModel ), options );

    // @public (tandem) Tandem support, should be a novel name to reduce the risk of parent or child collisions
    this.roundStickyToggleButtonTandem = options.tandem;
    this.roundStickyToggleButtonTandem && this.roundStickyToggleButtonTandem.addInstance( this );
  }

  sun.register( 'RoundStickyToggleButton', RoundStickyToggleButton, {

    // @public
    dispose: function() {
      //TODO implement this, see sun#212
    }
  } );

  return inherit( RoundButtonView, RoundStickyToggleButton );
} );<commit_msg>Added type for RoundStickyToggleButton<commit_after>// Copyright 2014-2015, University of Colorado Boulder

/**
 * A round toggle button that switches the value of a property that can take on valueUp or valueDown.  It
 * sticks in the down position when pressed, popping back up when pressed
 * again.
 *
 * @author John Blanco
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var RoundButtonView = require( 'SUN/buttons/RoundButtonView' );
  var StickyToggleButtonInteractionStateProperty = require( 'SUN/buttons/StickyToggleButtonInteractionStateProperty' );
  var StickyToggleButtonModel = require( 'SUN/buttons/StickyToggleButtonModel' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );

  // phet-io types
  var TToggleButton = require( 'PHET_IO/types/sun/buttons/TToggleButton' );

  /**
   * @param {Object} valueUp value when the toggle is in the 'up' position
   * @param {Object} valueDown value when the toggle is in the 'down' position
   * @param {Property} property axon property that can be either valueUp or valueDown.
   * @param {Object} [options]
   * @constructor
   */
  function RoundStickyToggleButton( valueUp, valueDown, property, options ) {

    Tandem.validateOptions( options ); // The tandem is required when brand==='phet-io'

    var buttonModel = new StickyToggleButtonModel( valueUp, valueDown, property );
    RoundButtonView.call( this, buttonModel, new StickyToggleButtonInteractionStateProperty( buttonModel ), options );

    // @public (tandem) Tandem support, should be a novel name to reduce the risk of parent or child collisions
    this.roundStickyToggleButtonTandem = options.tandem;
    TToggleButton && this.roundStickyToggleButtonTandem && this.roundStickyToggleButtonTandem.addInstance( this, TToggleButton );
  }

  sun.register( 'RoundStickyToggleButton', RoundStickyToggleButton, {

    // @public
    dispose: function() {
      //TODO implement this, see sun#212
    }
  } );

  return inherit( RoundButtonView, RoundStickyToggleButton );
} );<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief Arango statements
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var internal = require("internal");

// -----------------------------------------------------------------------------
// --SECTION--                                                   ArangoStatement
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// --SECTION--                                      constructors and destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoStatement
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////

function ArangoStatement (database, data) {
  this._database = database;
  this._doCount = false;
  this._batchSize = null;
  this._bindVars = {};

  if (!(data instanceof Object)) {
    throw "ArangoStatement needs initial data";
  }

  if (data.query === undefined || data.query === "") {
    throw "ArangoStatement needs a valid query attribute";
  }
  this.setQuery(data.query);

  if (data.count !== undefined) {
    this.setCount(data.count);
  }
  if (data.batchSize !== undefined) {
    this.setBatchSize(data.batchSize);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief bind a parameter to the statement
///
/// This function can be called multiple times, once for each bind parameter.
/// All bind parameters will be transferred to the server in one go when
/// execute() is called.
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.bind = function (key, value) {
  if (key instanceof Object) {
    if (value !== undefined) {
      throw "invalid bind parameter declaration";
    }

    this._bindVars = key;
  }
  else if (typeof(key) === "string") {
    if (this._bindVars[key] !== undefined) {
      throw "redeclaration of bind parameter";
    }

    this._bindVars[key] = value;
  }
  else if (typeof(key) === "number") {
    var strKey = String(parseInt(key));

    if (strKey !== String(key)) {
      throw "invalid bind parameter declaration";
    }

    if (this._bindVars[strKey] !== undefined) {
      throw "redeclaration of bind parameter";
    }

    this._bindVars[strKey] = value;
  }
  else {
    throw "invalid bind parameter declaration";
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief return the bind variables already set
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.getBindVariables = function () {
  return this._bindVars;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief get the count flag for the statement
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.getCount = function () {
  return this._doCount;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief get the maximum number of results documents the cursor will return
/// in a single server roundtrip.
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.getBatchSize = function () {
  return this._batchSize;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief get query string
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.getQuery = function () {
  return this._query;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief set the count flag for the statement
///
/// Setting the count flag will make the statement's result cursor return the
/// total number of result documents. The count flag is not set by default.
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.setCount = function (bool) {
  this._doCount = bool ? true : false;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief set the maximum number of results documents the cursor will return
/// in a single server roundtrip.
/// The higher this number is, the less server roundtrips will be made when
/// iterating over the result documents of a cursor.
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.setBatchSize = function (value) {
  if (parseInt(value) > 0) {
    this._batchSize = parseInt(value);
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief set the query string
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.setQuery = function (query) {
  this._query = query;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief parse a query and return the results
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.parse = function () {
  throw "cannot call abstract method parse()";
};

////////////////////////////////////////////////////////////////////////////////
/// @brief explain a query and return the results
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.explain = function () {
  throw "cannot call abstract method explain()";
};

////////////////////////////////////////////////////////////////////////////////
/// @brief execute the query
///
/// Invoking execute() will transfer the query and all bind parameters to the
/// server. It will return a cursor with the query results in case of success.
/// In case of an error, the error will be printed
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.execute = function () {
  throw "cannot call abstract method execute()";
};

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                    MODULE EXPORTS
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoStatement
/// @{
////////////////////////////////////////////////////////////////////////////////

exports.ArangoStatement = ArangoStatement;

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<commit_msg>removed "redeclaration of bind parameter"<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief Arango statements
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var internal = require("internal");

// -----------------------------------------------------------------------------
// --SECTION--                                                   ArangoStatement
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// --SECTION--                                      constructors and destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoStatement
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////

function ArangoStatement (database, data) {
  this._database = database;
  this._doCount = false;
  this._batchSize = null;
  this._bindVars = {};

  if (!(data instanceof Object)) {
    throw "ArangoStatement needs initial data";
  }

  if (data.query === undefined || data.query === "") {
    throw "ArangoStatement needs a valid query attribute";
  }
  this.setQuery(data.query);

  if (data.count !== undefined) {
    this.setCount(data.count);
  }
  if (data.batchSize !== undefined) {
    this.setBatchSize(data.batchSize);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief bind a parameter to the statement
///
/// This function can be called multiple times, once for each bind parameter.
/// All bind parameters will be transferred to the server in one go when
/// execute() is called.
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.bind = function (key, value) {
  if (key instanceof Object) {
    if (value !== undefined) {
      throw "invalid bind parameter declaration";
    }

    this._bindVars = key;
  }
  else if (typeof(key) === "string") {
    this._bindVars[key] = value;
  }
  else if (typeof(key) === "number") {
    var strKey = String(parseInt(key));

    if (strKey !== String(key)) {
      throw "invalid bind parameter declaration";
    }

    this._bindVars[strKey] = value;
  }
  else {
    throw "invalid bind parameter declaration";
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief return the bind variables already set
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.getBindVariables = function () {
  return this._bindVars;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief get the count flag for the statement
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.getCount = function () {
  return this._doCount;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief get the maximum number of results documents the cursor will return
/// in a single server roundtrip.
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.getBatchSize = function () {
  return this._batchSize;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief get query string
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.getQuery = function () {
  return this._query;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief set the count flag for the statement
///
/// Setting the count flag will make the statement's result cursor return the
/// total number of result documents. The count flag is not set by default.
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.setCount = function (bool) {
  this._doCount = bool ? true : false;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief set the maximum number of results documents the cursor will return
/// in a single server roundtrip.
/// The higher this number is, the less server roundtrips will be made when
/// iterating over the result documents of a cursor.
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.setBatchSize = function (value) {
  if (parseInt(value) > 0) {
    this._batchSize = parseInt(value);
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief set the query string
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.setQuery = function (query) {
  this._query = query;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief parse a query and return the results
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.parse = function () {
  throw "cannot call abstract method parse()";
};

////////////////////////////////////////////////////////////////////////////////
/// @brief explain a query and return the results
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.explain = function () {
  throw "cannot call abstract method explain()";
};

////////////////////////////////////////////////////////////////////////////////
/// @brief execute the query
///
/// Invoking execute() will transfer the query and all bind parameters to the
/// server. It will return a cursor with the query results in case of success.
/// In case of an error, the error will be printed
////////////////////////////////////////////////////////////////////////////////

ArangoStatement.prototype.execute = function () {
  throw "cannot call abstract method execute()";
};

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                    MODULE EXPORTS
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoStatement
/// @{
////////////////////////////////////////////////////////////////////////////////

exports.ArangoStatement = ArangoStatement;

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<|endoftext|>


<commit_before>/* global describe, it */
var LogEmitter = require('../../lib/LogEmitter');
var expect = require('unexpected');
var async = require('async');

var EventEmitter = require('events').EventEmitter;

describe('lib/LogEmitter', function () {
    it('should export a function', function () {
        expect(LogEmitter, 'to be a function');
    });
    it('should be an instance of EventEmitter', function () {
        var logEmitter = new LogEmitter();
        expect(logEmitter, 'to be an', EventEmitter);
    });
    it('should emit an object when a string is passed as payload', function (done) {
        var string = 'log message';
        var logEmitter = new LogEmitter();
        logEmitter.on('log', function (e) {
            expect(e, 'to satisfy', {
                type: 'log',
                message: 'log message'
            });
            done();
        });
        logEmitter.emit('log', string);
    });
    it('should leave non log events alone', function (done) {
        async.each([
            'foo',
            'open',
            'readable',
            'connect',
            'listener',
            'data',
            'info'
        ], function (eventName, callback) {
            var logEmitter = new LogEmitter();
            logEmitter.on(eventName, function (e) {
                try {
                    expect(e, 'to equal', 'this is the ' + eventName + ' event');
                } catch (e) {
                    return callback(e);
                }
                callback();
            });
            logEmitter.emit(eventName, 'this is the ' + eventName + ' event');
        }, done);
    });
    describe('severity methods', function () {
        ['log', 'info', 'debug', 'error'].forEach(function (severity) {
            describe('.' + severity, function () {
                it('should emit an event with severity ' + severity, function (done) {
                    var logEmitter = new LogEmitter();
                    logEmitter.on('log', function (e) {
                        expect(e, 'to satisfy', {
                            severity: severity,
                            message: 'the log message'
                        });
                        done();
                    });
                    logEmitter[severity]('the log message');
                });
                it('should take multiple args', function (done) {
                    var logEmitter = new LogEmitter();
                    logEmitter.on('log', function (e) {
                        expect(e, 'to have property', 'message', 'the, log, message');
                        done();
                    });
                    logEmitter[severity]('the', 'log', 'message');
                });
                it('should take an object as arg', function (done) {
                    var logEmitter = new LogEmitter();
                    logEmitter.on('log', function (e) {
                        expect(e, 'to equal', {
                            type: 'log',
                            severity: severity,
                            message: 'the log message',
                            anotherProp: true
                        });
                        done();
                    });
                    logEmitter[severity]({ message: 'the log message', anotherProp: true });
                });
            });
        });
    });
    describe('baseLogObject', function () {
        it('should add basic properties to the emitted object', function (done) {
            var logEmitter = new LogEmitter();
            logEmitter.on('log', function (e) {
                expect(e, 'to satisfy', {
                    type: 'log',
                    message: 'a log message'
                });
                done();
            });
            logEmitter.emit('log', { message: 'a log message' });
        });
        it('should be able to extend the baseLogObject', function (done) {
            var logEmitter = new LogEmitter();
            logEmitter.on('log', function (e) {
                expect(e, 'to satisfy', {
                    message: 'a log message',
                    extraAttribute: true
                });
                done();
            });
            logEmitter.logBaseObject = {
                extraAttribute: true
            };
            logEmitter.emit('log', { message: 'a log message' });
        });
        it('should keep the defaults when adding extra attributes', function (done) {
            var logEmitter = new LogEmitter();
            logEmitter.on('log', function (e) {
                expect(e, 'to satisfy', {
                    type: 'log',
                    message: 'a log message',
                    extraAttribute: true
                });
                done();
            });
            logEmitter.logBaseObject = {
                extraAttribute: true
            };
            logEmitter.emit('log', { message: 'a log message' });
        });
    });
    describe('.relay', function () {
        it('relaying events from one emitter to another', function (done) {
            var logEmitterOne = new LogEmitter();
            var logEmitterTwo = new LogEmitter();

            logEmitterOne.relay(logEmitterTwo);

            logEmitterTwo.on('log', function (e) {
                expect(e, 'to have property', 'message', 'hey there!');
                done();
            });

            logEmitterOne.log('hey there!');
        });
        it('should be able to relay methods from augmented functions', function (done) {
            var func = LogEmitter.augmentFunction(function () {
                this.log('I was called');
            });
            var logEmitter = new LogEmitter();
            func.relay(logEmitter);

            logEmitter.on('log', function (e) {
                expect(e, 'to equal', {
                    severity: 'log',
                    type: 'log',
                    message: 'I was called'
                });
                done();
            });

            func();
        });
    });
    describe('.augmentFunction', function () {
        it('should be able to augment a function with LogEmitter behaviour', function (done) {
            var func = LogEmitter.augmentFunction(function (arg) {
                this.log({ message: 'was called', value: arg });
            });

            func.on('log', function (e) {
                expect(e, 'to equal', {
                    severity: 'log',
                    type: 'log',
                    message: 'was called',
                    value: 'foobar'
                });
                done();
            });

            func('foobar');
        });
        it('should be able to set the logBaseObject', function (done) {
            var func = LogEmitter.augmentFunction(function (arg) {
                this.logBaseObject = {
                    iAm: 'augmented'
                };
                this.log({ message: 'was called', value: arg });
            });

            func.on('log', function (e) {
                expect(e, 'to equal', {
                    iAm: 'augmented',
                    severity: 'log',
                    type: 'log',
                    message: 'was called',
                    value: 'foobar'
                });
                done();
            });

            func('foobar');
        });
        it('should be able to relay methods from other logemitters', function (done) {
            var func = LogEmitter.augmentFunction(function () {
                this.logBaseObject = {
                    iAmAugmented: true
                };
            });
            var logEmitter = new LogEmitter();
            logEmitter.relay(func);

            func.on('log', function (e) {
                expect(e, 'to equal', {
                    iAmAugmented: true,
                    severity: 'log',
                    type: 'log',
                    message: 'logged from logEmitter'
                });
                done();
            });

            func();
            logEmitter.log('logged from logEmitter');
        });
    });
    describe('.report', function () {
        it('should report log lines to the given destination', function () {
            var latestLogMessage = null;
            var reporter = {
                log: function (x) { latestLogMessage = x; }
            };
            var logEmitter = new LogEmitter();
            logEmitter.report(reporter);
            logEmitter.log('foobar');
            expect(latestLogMessage, 'to equal', 'foobar');
        });
        it('should return a method to stop reporting', function () {
            var latestLogMessage = null;
            var reporter = {
                log: function (x) { latestLogMessage = x; }
            };
            var logEmitter = new LogEmitter();
            var stopReporting = logEmitter.report(reporter);
            logEmitter.log('foobar');
            expect(latestLogMessage, 'to equal', 'foobar');
            stopReporting();
            logEmitter.log('qux!');
            expect(latestLogMessage, 'to equal', 'foobar');
        });
        describe('supported reporting severities', function () {
            ['log', 'info', 'warn', 'error'].forEach(function (severity) {
                it('severity "' + severity + '" should correspond to method .' + severity + ' on the reporter', function () {
                    var latestLogMessage = null;
                    var reporter = {};
                    reporter[severity] = function (x) { latestLogMessage = x; };
                    var logEmitter = new LogEmitter();
                    logEmitter.report(reporter);
                    logEmitter.emit('log', {
                        severity: severity,
                        message: 'foobar'
                    });
                    expect(latestLogMessage, 'not to be null');
                });
            });
            it('severity "warning" should correspond to the .warn method on the reporter', function () {
                    var latestLogMessage = null;
                    var reporter = {};
                    reporter.warn = function (x) { latestLogMessage = x; };
                    var logEmitter = new LogEmitter();
                    logEmitter.report(reporter);
                    logEmitter.emit('log', {
                        severity: 'warning',
                        message: 'foobar'
                    });
                    expect(latestLogMessage, 'not to be null');
            });
            it('severity "foobar" should correspond to the .info method on the reporter', function () {
                    var latestLogMessage = null;
                    var reporter = {};
                    reporter.info = function (x) { latestLogMessage = x; };
                    var logEmitter = new LogEmitter();
                    logEmitter.report(reporter);
                    logEmitter.emit('log', {
                        severity: 'foobar',
                        message: 'foobar'
                    });
                    expect(latestLogMessage, 'not to be null');
            });
        });
        describe('reporting properties', function () {
            [
                {
                    input: {
                        message: 'foo bar',
                        prop: true
                    },
                    expected: 'foo bar prop: true'
                },
                {
                    input: {
                        message: 'A pretty error message.',
                        handler: 'test',
                        method: 'GET'
                    },
                    expected: 'A pretty error message. method: GET, handler: test'
                }
            ].forEach(function (testCase) {
                it(testCase.expected, function () {
                    var latestLogMessage = null;
                    var reporter = {
                        log: function (x) { latestLogMessage = x; }
                    };
                    var logEmitter = new LogEmitter();
                    logEmitter.report(reporter);
                    logEmitter.log(testCase.input);
                    expect(latestLogMessage, 'to equal', testCase.expected);
                });
            });
        });
    });
});
<commit_msg>fix indentation in tests<commit_after>/* global describe, it */
var LogEmitter = require('../../lib/LogEmitter');
var expect = require('unexpected');
var async = require('async');

var EventEmitter = require('events').EventEmitter;

describe('lib/LogEmitter', function () {
    it('should export a function', function () {
        expect(LogEmitter, 'to be a function');
    });
    it('should be an instance of EventEmitter', function () {
        var logEmitter = new LogEmitter();
        expect(logEmitter, 'to be an', EventEmitter);
    });
    it('should emit an object when a string is passed as payload', function (done) {
        var string = 'log message';
        var logEmitter = new LogEmitter();
        logEmitter.on('log', function (e) {
            expect(e, 'to satisfy', {
                type: 'log',
                message: 'log message'
            });
            done();
        });
        logEmitter.emit('log', string);
    });
    it('should leave non log events alone', function (done) {
        async.each([
            'foo',
            'open',
            'readable',
            'connect',
            'listener',
            'data',
            'info'
        ], function (eventName, callback) {
            var logEmitter = new LogEmitter();
            logEmitter.on(eventName, function (e) {
                try {
                    expect(e, 'to equal', 'this is the ' + eventName + ' event');
                } catch (e) {
                    return callback(e);
                }
                callback();
            });
            logEmitter.emit(eventName, 'this is the ' + eventName + ' event');
        }, done);
    });
    describe('severity methods', function () {
        ['log', 'info', 'debug', 'error'].forEach(function (severity) {
            describe('.' + severity, function () {
                it('should emit an event with severity ' + severity, function (done) {
                    var logEmitter = new LogEmitter();
                    logEmitter.on('log', function (e) {
                        expect(e, 'to satisfy', {
                            severity: severity,
                            message: 'the log message'
                        });
                        done();
                    });
                    logEmitter[severity]('the log message');
                });
                it('should take multiple args', function (done) {
                    var logEmitter = new LogEmitter();
                    logEmitter.on('log', function (e) {
                        expect(e, 'to have property', 'message', 'the, log, message');
                        done();
                    });
                    logEmitter[severity]('the', 'log', 'message');
                });
                it('should take an object as arg', function (done) {
                    var logEmitter = new LogEmitter();
                    logEmitter.on('log', function (e) {
                        expect(e, 'to equal', {
                            type: 'log',
                            severity: severity,
                            message: 'the log message',
                            anotherProp: true
                        });
                        done();
                    });
                    logEmitter[severity]({ message: 'the log message', anotherProp: true });
                });
            });
        });
    });
    describe('baseLogObject', function () {
        it('should add basic properties to the emitted object', function (done) {
            var logEmitter = new LogEmitter();
            logEmitter.on('log', function (e) {
                expect(e, 'to satisfy', {
                    type: 'log',
                    message: 'a log message'
                });
                done();
            });
            logEmitter.emit('log', { message: 'a log message' });
        });
        it('should be able to extend the baseLogObject', function (done) {
            var logEmitter = new LogEmitter();
            logEmitter.on('log', function (e) {
                expect(e, 'to satisfy', {
                    message: 'a log message',
                    extraAttribute: true
                });
                done();
            });
            logEmitter.logBaseObject = {
                extraAttribute: true
            };
            logEmitter.emit('log', { message: 'a log message' });
        });
        it('should keep the defaults when adding extra attributes', function (done) {
            var logEmitter = new LogEmitter();
            logEmitter.on('log', function (e) {
                expect(e, 'to satisfy', {
                    type: 'log',
                    message: 'a log message',
                    extraAttribute: true
                });
                done();
            });
            logEmitter.logBaseObject = {
                extraAttribute: true
            };
            logEmitter.emit('log', { message: 'a log message' });
        });
    });
    describe('.relay', function () {
        it('relaying events from one emitter to another', function (done) {
            var logEmitterOne = new LogEmitter();
            var logEmitterTwo = new LogEmitter();

            logEmitterOne.relay(logEmitterTwo);

            logEmitterTwo.on('log', function (e) {
                expect(e, 'to have property', 'message', 'hey there!');
                done();
            });

            logEmitterOne.log('hey there!');
        });
        it('should be able to relay methods from augmented functions', function (done) {
            var func = LogEmitter.augmentFunction(function () {
                this.log('I was called');
            });
            var logEmitter = new LogEmitter();
            func.relay(logEmitter);

            logEmitter.on('log', function (e) {
                expect(e, 'to equal', {
                    severity: 'log',
                    type: 'log',
                    message: 'I was called'
                });
                done();
            });

            func();
        });
    });
    describe('.augmentFunction', function () {
        it('should be able to augment a function with LogEmitter behaviour', function (done) {
            var func = LogEmitter.augmentFunction(function (arg) {
                this.log({ message: 'was called', value: arg });
            });

            func.on('log', function (e) {
                expect(e, 'to equal', {
                    severity: 'log',
                    type: 'log',
                    message: 'was called',
                    value: 'foobar'
                });
                done();
            });

            func('foobar');
        });
        it('should be able to set the logBaseObject', function (done) {
            var func = LogEmitter.augmentFunction(function (arg) {
                this.logBaseObject = {
                    iAm: 'augmented'
                };
                this.log({ message: 'was called', value: arg });
            });

            func.on('log', function (e) {
                expect(e, 'to equal', {
                    iAm: 'augmented',
                    severity: 'log',
                    type: 'log',
                    message: 'was called',
                    value: 'foobar'
                });
                done();
            });

            func('foobar');
        });
        it('should be able to relay methods from other logemitters', function (done) {
            var func = LogEmitter.augmentFunction(function () {
                this.logBaseObject = {
                    iAmAugmented: true
                };
            });
            var logEmitter = new LogEmitter();
            logEmitter.relay(func);

            func.on('log', function (e) {
                expect(e, 'to equal', {
                    iAmAugmented: true,
                    severity: 'log',
                    type: 'log',
                    message: 'logged from logEmitter'
                });
                done();
            });

            func();
            logEmitter.log('logged from logEmitter');
        });
    });
    describe('.report', function () {
        it('should report log lines to the given destination', function () {
            var latestLogMessage = null;
            var reporter = {
                log: function (x) { latestLogMessage = x; }
            };
            var logEmitter = new LogEmitter();
            logEmitter.report(reporter);
            logEmitter.log('foobar');
            expect(latestLogMessage, 'to equal', 'foobar');
        });
        it('should return a method to stop reporting', function () {
            var latestLogMessage = null;
            var reporter = {
                log: function (x) { latestLogMessage = x; }
            };
            var logEmitter = new LogEmitter();
            var stopReporting = logEmitter.report(reporter);
            logEmitter.log('foobar');
            expect(latestLogMessage, 'to equal', 'foobar');
            stopReporting();
            logEmitter.log('qux!');
            expect(latestLogMessage, 'to equal', 'foobar');
        });
        describe('supported reporting severities', function () {
            ['log', 'info', 'warn', 'error'].forEach(function (severity) {
                it('severity "' + severity + '" should correspond to method .' + severity + ' on the reporter', function () {
                    var latestLogMessage = null;
                    var reporter = {};
                    reporter[severity] = function (x) { latestLogMessage = x; };
                    var logEmitter = new LogEmitter();
                    logEmitter.report(reporter);
                    logEmitter.emit('log', {
                        severity: severity,
                        message: 'foobar'
                    });
                    expect(latestLogMessage, 'not to be null');
                });
            });
            it('severity "warning" should correspond to the .warn method on the reporter', function () {
                var latestLogMessage = null;
                var reporter = {};
                reporter.warn = function (x) { latestLogMessage = x; };
                var logEmitter = new LogEmitter();
                logEmitter.report(reporter);
                logEmitter.emit('log', {
                    severity: 'warning',
                    message: 'foobar'
                });
                expect(latestLogMessage, 'not to be null');
            });
            it('severity "foobar" should correspond to the .info method on the reporter', function () {
                var latestLogMessage = null;
                var reporter = {};
                reporter.info = function (x) { latestLogMessage = x; };
                var logEmitter = new LogEmitter();
                logEmitter.report(reporter);
                logEmitter.emit('log', {
                    severity: 'foobar',
                    message: 'foobar'
                });
                expect(latestLogMessage, 'not to be null');
            });
        });
        describe('reporting properties', function () {
            [
                {
                    input: {
                        message: 'foo bar',
                        prop: true
                    },
                    expected: 'foo bar prop: true'
                },
                {
                    input: {
                        message: 'A pretty error message.',
                        handler: 'test',
                        method: 'GET'
                    },
                    expected: 'A pretty error message. method: GET, handler: test'
                }
            ].forEach(function (testCase) {
                it(testCase.expected, function () {
                    var latestLogMessage = null;
                    var reporter = {
                        log: function (x) { latestLogMessage = x; }
                    };
                    var logEmitter = new LogEmitter();
                    logEmitter.report(reporter);
                    logEmitter.log(testCase.input);
                    expect(latestLogMessage, 'to equal', testCase.expected);
                });
            });
        });
    });
});
<|endoftext|>