<commit_before>// This module manages the page tree and contains the wildcard
// Express route that actually serves pages. That route is installed
// at the very end of the process, in an `afterInit` callback,
// which is late enough to allow all other modules to add routes first.
//
// Also implements parked pages, "plain old page types" (those that aren't
// powered by a module), the context menu (big gear menu) and the publish menu.
//
// ## Options
//
// **`types`: specifies the page types that can be chosen for a page.** This list must
// include all page types that will be present in the tree (not piece types).
//
// The default setting is:
//
//```javascript
//  types: [
//    {
//      name: 'home',
//      label: 'Home'
//    },
//    {
//      name: 'default',
//      label: 'Default'
//    }
//  ]
//```
//
// The `home` page type is required.
//
// **`contextMenu`: specifies the default offerings on the context menu.** These
// can also be overridden for any request by setting `req.contextMenu` to an array
// in the same format.
//
// The default setting is:
//
//```javascript
// contextMenu: [
//   {
//     action: 'insert-page',
//     label: 'New Page'
//   },
//   {
//     action: 'update-page',
//     label: 'Page Settings'
//   },
//   {
//     action: 'versions-page',
//     label: 'Page Versions'
//   },
//   {
//     action: 'trash-page',
//     label: 'Move to Trash'
//   },
//   {
//     action: 'reorganize-page',
//     label: 'Reorganize',
//     // Until we port the provisions for non-admins to reorganize
//     // over from 0.5
//     permission: 'admin'
//   }
// ]
//```
//
// The `action` becomes a `data-apos-ACTIONGOESHERE` attribute on the
// menu item. If `permission` is set, the item is only shown to users
// with that permission (this is NOT sufficient protection for the
// backend routes it may access, they must also be secured).
//
// **`publishMenu`: configures the publication menu,** which appears
// only if the current page is unpublished or `data.pieces` is present
// and is unpublished. Syntax is identical to `contextMenu`. The default
// setting is:
//
//```javascript
// publishMenu: [
//   {
//     action: 'publish-page',
//     label: 'Publish Page'
//   }
// ]
//```
//
// Again, you can override it by setting `req.publishMenu`.
//
// If you are looking for the schema fields common to all pages in the tree,
// check out the [apostrophe-custom-pages](../apostrophe-custom-pages/index.html)
// module, which all page types extend, including "ordinary" pages.
//
// **`park`: configures certain pages to be automatically created and refreshed
// whenever the site starts up.** The parked pages you get are actually the
// concatenation of the `minimumPark` and `park` options.
//
// `minimumPark` has a default, which you will typically leave unchanged:
//
//```javascript
// [
//   {
//     slug: '/',
//     published: true,
//     _defaults: {
//       title: 'Home',
//       type: 'home'
//     },
//     _children: [
//       {
//         slug: '/trash',
//         type: 'trash',
//         trash: true,
//         published: false,
//         orphan: true,
//         _defaults: {
//           title: 'Trash'
//         },
//       }
//     ]
//   },
// ]
//```
//
// * The `park` and `minimumPark` options are arrays. Each array is a
// page to be created or recreated on startup.
//
// * If a page has a `parent` property, it is created as a child
// of the page whose `slug` property equals `parent`. If a page has no
// `parent` property and it is not the home page itself, it is created as a
// child of the home page.
//
// * Any other properties that do not begin with a `_` are automatically
// refreshed on the page object in the database at startup time.
//
// * If a page has a `_children` array property, these are additional parked pages,
// created as children of the page.
//
// * The properties of the `_default` option are applied to the page object *only
// at creation time*, meaning that changes users make to them later will stick.
//
// * `orphan: true` prevents a page from appearing in standard navigation links based
// on parent-child relationships
// (as opposed to hand-built navigation widgets powered by joins and the like).
//
// * The "page settings" UI is evolving toward not allowing users to
// modify properties that are explicitly set via `park` (rather than being set once
// via `_defaults`). In any case such properties are reset by restarts.
//
// **`filters`: Apostrophe cursor filters applied when fetching the current page.**
// The default settings ensure that `req.data.page` has a `_children` property
// and an `_ancestors` property:
//
//```javascript
// {
//   // Get the kids of the ancestors too so we can do tabs and accordion nav
//   ancestors: { children: true },
//   // Get our own kids
//   children: true
// };
//```
//
// See the [apostrophe-pages-cursor](server-apostrophe-pages-cursor.html) type for additional
// cursor filters and options you might wish to configure, such as adding
// a `depth` option to `children`.
//
// **`deleteFromTrash`: if set to `true`, Apostrophe offers a button in the
// "reorganize" view to permanently delete pages that are already in the trash.**
// This option defaults to `false` because, in our experience, customers usually
// ask for a way to "un-empty the trash," and of course there isn't one. We don't
// recommend enabling the feature on a permanent basis but it can be useful during
// the early stages of site population.

var _ = require('lodash');
var async = require('async');

module.exports = {

  alias: 'pages',

  types: [
    {
      // So that the minimum parked pages don't result in an error when home has no manager. -Tom
      name: 'home',
      label: 'Home'
    },
    {
      // So that unit tests have an easy page type to insert and A2 is less weird out of the box.
      name: 'default',
      label: 'Default'
    }
  ],

  contextMenu: [
    {
      action: 'insert-page',
      label: 'New Page'
    },
    {
      action: 'copy-page',
      label: 'Copy Page'
    },
    {
      action: 'update-page',
      label: 'Page Settings'
    },
    {
      action: 'versions-page',
      label: 'Page Versions'
    },
    {
      action: 'trash-page',
      label: 'Move to Trash'
    },
    {
      action: 'reorganize-page',
      label: 'Reorganize'
    }
  ],

  publishMenu: [
    {
      action: 'publish-page',
      label: 'Publish Page'
    }
  ],

  afterConstruct: function(self, callback) {
    self.pushAssets();
    self.validateTypeChoices();
    return async.series([
      self.registerTrashPageType,
      self.ensureIndexes
    ], callback);
  },

  construct: function(self, options) {

    self.typeChoices = options.types || [];

    require('./lib/helpers.js')(self, options);
    require('./lib/browser.js')(self, options);
    require('./lib/routes.js')(self, options);
    require('./lib/api.js')(self, options);

    // merge new methods with all apostrophe-cursors
    self.apos.define('apostrophe-cursor', require('./lib/anyCursor.js'));

    // Add methods to cursors used specifically to fetch pages only
    self.apos.define('apostrophe-pages-cursor', require('./lib/pagesCursor.js'));

    // When all modules are ready, invoke `registerGenericPageTypes` to register a manager
    // for any page type that doesn't already have one via `apostrophe-custom-pages`,
    // `apostrophe-pieces-pages`, etc.

    self.modulesReady = function(callback) {
      return async.series([
        self.registerGenericPageTypes,
        self.manageOrphans
      ], callback);
    };

    // Wait until the last possible moment to add
    // the wildcard route for serving pages, so that
    // other routes are not blocked

    self.afterInit = function(callback) {
      self.apos.app.get('*', self.serve);
      return async.series([
        self.implementParkAll
      ], callback);
    };

  },

};
<commit_msg>document new home option<commit_after>// This module manages the page tree and contains the wildcard
// Express route that actually serves pages. That route is installed
// at the very end of the process, in an `afterInit` callback,
// which is late enough to allow all other modules to add routes first.
//
// Also implements parked pages, "plain old page types" (those that aren't
// powered by a module), the context menu (big gear menu) and the publish menu.
//
// ## Options
//
// **`types`: specifies the page types that can be chosen for a page.** This list must
// include all page types that will be present in the tree (not piece types).
//
// The default setting is:
//
//```javascript
//  types: [
//    {
//      name: 'home',
//      label: 'Home'
//    },
//    {
//      name: 'default',
//      label: 'Default'
//    }
//  ]
//```
//
// The `home` page type is required.
//
// **`contextMenu`: specifies the default offerings on the context menu.** These
// can also be overridden for any request by setting `req.contextMenu` to an array
// in the same format.
//
// The default setting is:
//
//```javascript
// contextMenu: [
//   {
//     action: 'insert-page',
//     label: 'New Page'
//   },
//   {
//     action: 'update-page',
//     label: 'Page Settings'
//   },
//   {
//     action: 'versions-page',
//     label: 'Page Versions'
//   },
//   {
//     action: 'trash-page',
//     label: 'Move to Trash'
//   },
//   {
//     action: 'reorganize-page',
//     label: 'Reorganize',
//     // Until we port the provisions for non-admins to reorganize
//     // over from 0.5
//     permission: 'admin'
//   }
// ]
//```
//
// The `action` becomes a `data-apos-ACTIONGOESHERE` attribute on the
// menu item. If `permission` is set, the item is only shown to users
// with that permission (this is NOT sufficient protection for the
// backend routes it may access, they must also be secured).
//
// **`publishMenu`: configures the publication menu,** which appears
// only if the current page is unpublished or `data.pieces` is present
// and is unpublished. Syntax is identical to `contextMenu`. The default
// setting is:
//
//```javascript
// publishMenu: [
//   {
//     action: 'publish-page',
//     label: 'Publish Page'
//   }
// ]
//```
//
// Again, you can override it by setting `req.publishMenu`.
//
// If you are looking for the schema fields common to all pages in the tree,
// check out the [apostrophe-custom-pages](../apostrophe-custom-pages/index.html)
// module, which all page types extend, including "ordinary" pages.
//
// **`park`: configures certain pages to be automatically created and refreshed
// whenever the site starts up.** The parked pages you get are actually the
// concatenation of the `minimumPark` and `park` options.
//
// `minimumPark` has a default, which you will typically leave unchanged:
//
//```javascript
// [
//   {
//     slug: '/',
//     published: true,
//     _defaults: {
//       title: 'Home',
//       type: 'home'
//     },
//     _children: [
//       {
//         slug: '/trash',
//         type: 'trash',
//         trash: true,
//         published: false,
//         orphan: true,
//         _defaults: {
//           title: 'Trash'
//         },
//       }
//     ]
//   },
// ]
//```
//
// * The `park` and `minimumPark` options are arrays. Each array is a
// page to be created or recreated on startup.
//
// * If a page has a `parent` property, it is created as a child
// of the page whose `slug` property equals `parent`. If a page has no
// `parent` property and it is not the home page itself, it is created as a
// child of the home page.
//
// * Any other properties that do not begin with a `_` are automatically
// refreshed on the page object in the database at startup time.
//
// * If a page has a `_children` array property, these are additional parked pages,
// created as children of the page.
//
// * The properties of the `_default` option are applied to the page object *only
// at creation time*, meaning that changes users make to them later will stick.
//
// * `orphan: true` prevents a page from appearing in standard navigation links based
// on parent-child relationships
// (as opposed to hand-built navigation widgets powered by joins and the like).
//
// * The "page settings" UI is evolving toward not allowing users to
// modify properties that are explicitly set via `park` (rather than being set once
// via `_defaults`). In any case such properties are reset by restarts.
//
// **`filters`: Apostrophe cursor filters applied when fetching the current page.**
// The default settings ensure that `req.data.page` has a `_children` property
// and an `_ancestors` property:
//
//```javascript
// {
//   // Get the kids of the ancestors too so we can do tabs and accordion nav
//   ancestors: { children: true },
//   // Get our own kids
//   children: true
// };
//```
//
// See the [apostrophe-pages-cursor](server-apostrophe-pages-cursor.html) type for additional
// cursor filters and options you might wish to configure, such as adding
// a `depth` option to `children`.
//
// ** `home`: Apostrophe populates the home page from `req.page._ancestors[0]` if possible.
// If not, Apostrophe fetches the home page separately, using the same filters configured for
// ancestors. You can shut this extra query off:
//
//```javascript
// {
//   home: false
// }
//```
//
// In addition, if ancestors are not configured, Apostrophe assumes you want the children
// of the home page. You can shut that off, and still get the home page:
//
//```javascript
// {
//   home: {
//     children: false
//   }
// }
//```
//
// **`deleteFromTrash`: if set to `true`, Apostrophe offers a button in the
// "reorganize" view to permanently delete pages that are already in the trash.**
// This option defaults to `false` because, in our experience, customers usually
// ask for a way to "un-empty the trash," and of course there isn't one. We don't
// recommend enabling the feature on a permanent basis but it can be useful during
// the early stages of site population.

var _ = require('lodash');
var async = require('async');

module.exports = {

  alias: 'pages',

  types: [
    {
      // So that the minimum parked pages don't result in an error when home has no manager. -Tom
      name: 'home',
      label: 'Home'
    },
    {
      // So that unit tests have an easy page type to insert and A2 is less weird out of the box.
      name: 'default',
      label: 'Default'
    }
  ],

  contextMenu: [
    {
      action: 'insert-page',
      label: 'New Page'
    },
    {
      action: 'copy-page',
      label: 'Copy Page'
    },
    {
      action: 'update-page',
      label: 'Page Settings'
    },
    {
      action: 'versions-page',
      label: 'Page Versions'
    },
    {
      action: 'trash-page',
      label: 'Move to Trash'
    },
    {
      action: 'reorganize-page',
      label: 'Reorganize'
    }
  ],

  publishMenu: [
    {
      action: 'publish-page',
      label: 'Publish Page'
    }
  ],

  afterConstruct: function(self, callback) {
    self.pushAssets();
    self.validateTypeChoices();
    return async.series([
      self.registerTrashPageType,
      self.ensureIndexes
    ], callback);
  },

  construct: function(self, options) {

    self.typeChoices = options.types || [];

    require('./lib/helpers.js')(self, options);
    require('./lib/browser.js')(self, options);
    require('./lib/routes.js')(self, options);
    require('./lib/api.js')(self, options);

    // merge new methods with all apostrophe-cursors
    self.apos.define('apostrophe-cursor', require('./lib/anyCursor.js'));

    // Add methods to cursors used specifically to fetch pages only
    self.apos.define('apostrophe-pages-cursor', require('./lib/pagesCursor.js'));

    // When all modules are ready, invoke `registerGenericPageTypes` to register a manager
    // for any page type that doesn't already have one via `apostrophe-custom-pages`,
    // `apostrophe-pieces-pages`, etc.

    self.modulesReady = function(callback) {
      return async.series([
        self.registerGenericPageTypes,
        self.manageOrphans
      ], callback);
    };

    // Wait until the last possible moment to add
    // the wildcard route for serving pages, so that
    // other routes are not blocked

    self.afterInit = function(callback) {
      self.apos.app.get('*', self.serve);
      return async.series([
        self.implementParkAll
      ], callback);
    };

  },

};
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013 David Evans, Killian Koenig
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// A component defines a small module of functionality and is
// initialized by adding it to an `Entity` or `Space`.
(function (TANK)
{
  "use strict";

  // ### Component constructor
  // Shouldn't really ever be called outside the engine.
  // If you are trying to register a new type of component,
  // use `TANK.registerComponent()`. If you are trying to add
  // a component to an entity, use `Entity.addComponent()`.
  //
  // name - The name of the component type.
  TANK.Component = function (name)
  {
    // ### Name of the component
    // `const string Component.name`
    this.name = name;

    // ### Parent Entity
    // `const Entity Component.parent`
    this.parent = null;
    this._functions = {};
    this._includes = [];
    this._interfaces = [];
  };

  // ### Instantiate a component
  // Creates a component object from this blueprint.
  // Will rarely be necessary to call outside the engine, as
  // this is dealt with internally in the `addComponent()` methods.
  //
  // - `return`: The new component object.
  TANK.Component.prototype.clone = function ()
  {
    var c = {};

    // Default functions
    c.construct = function () {};
    c.initialize = function () {};
    c.destruct = function () {};

    // Add interfaces
    c.interfaces = {};
    var i;
    for (i = 0; i < this._interfaces.length; ++i)
    {
      c.interfaces[this._interfaces[i]] = true;
    }

    // Add functions
    for (i in this._functions)
    {
      if (this._functions.hasOwnProperty(i))
      {
        c[i] = this._functions[i];
      }
    }

    // Set properties
    c.name = this.name;
    c._constructed = false;
    c.addEventListener = this.addEventListener;
    c.removeEventListener = this.removeEventListener;

    c._listeners = [];

    return c;
  };

  // ### Require other components
  // Defines a list of components that are required by this one
  // and will be automatically added before this one is added.
  // Components can be given as a string of comma seperated values,
  // a list of strings, or some combination of the above.

  // `Component Component.requires()`

  // - `return`: A reference to itself.
  TANK.Component.prototype.requires = function ()
  {
    var i, j, arg;
    for (i = 0; i < arguments.length; ++i)
    {
      arg = arguments[i];
      arg = arg.replace(/\s/g, "");
      var includes = arg.split(",");

      for (j = 0; j < includes.length; ++j)
      {
        this._includes.push(includes[j]);
      }
    }

    return this;
  };

  // ### Interfaces
  // Defines a list of interfaces that this component implements.
  // Manager components often look for components that implement a particular interface.
  // Common interfaces include `Drawable2D`, and `Collidable`
  // Interfaces can be given as a string of comma seperated values,
  // a list of strings, or some combination of the above.

  // `Component Component.interfaces()`

  // - `return`: A reference to itself.
  TANK.Component.prototype.interfaces = function ()
  {
    var i, j, arg;
    for (i = 0; i < arguments.length; ++i)
    {
      arg = arguments[i];
      arg = arg.replace(/\s/g, "");
      var interfaces = arg.split(",");

      for (j = 0; j < interfaces.length; ++j)
      {
        this._interfaces.push(interfaces[i]);
      }
    }

    return this;
  };

  // ### Set constructor function
  // Defines a function to be called after the component has been instantiated.
  // This is commonly where you would define member fields with default values.

  // `Component Component.construct(func)`

  // - `func`: A function to call after instantiation.
  // - `return`: A reference to itself.
  TANK.Component.prototype.construct = function (func)
  {
    this._functions.construct = func;
    return this;
  };

  // ### Set init function
  // Defines a function to be called after the component has been constructed and added to an entity.
  // This is commonly where you would listen for events and define other functions (e.g., `this.Foo = function(){}`)

  // `Component Component.initialize(func)`

  // - `func`: A function to call upon initialization.
  // - `return`: A reference to itself.
  TANK.Component.prototype.initialize = function (func)
  {
    this._functions.initialize = func;
    return this;
  };

  // ### Set uninit function
  // Defines a function to be called after the component has been removed from an entity.

  // `Component Component.destruct(func)`

  // - `func`: A function to call upon uninitialization.
  // - `return`: A reference to itself.
  TANK.Component.prototype.destruct = function (func)
  {
    this._functions.destruct = func;
    return this;
  };

  // ### Listen for an event
  // Registers a callback as a listener for a given event.
  // The event will be removed automatically when the component is
  // destructed.

  // `void Component.addEventListener(event, callback)`

  // - `event`: The name of the event.
  // - `callback`: A function to call when the event is triggered.
  TANK.Component.prototype.addEventListener = function (event, callback)
  {
    if (this._functions)
    {
      TANK.error("The component " + this.name + "tried to listen to " + event + "before it was instantiated.");
      return;
    }

    if (!this._constructed)
    {
      TANK.error("The component " + this.name + " tried to listen to " + event + " from `construct`, but should do so from `initialize` instead");
      return;
    }

    if (!callback || !callback.apply)
    {
      TANK.error("The component " + this.name + " tried to listen to " + event + " but did not supply a valid callback");
      return;
    }

    var listeners = this.space._events[event];
    if (!listeners)
    {
      listeners = [];
      this.space._events[event] = listeners;
    }

    this._listeners.push(
    {
      evt: event,
      self: this,
      func: callback
    });

    listeners.push(
    {
      self: this,
      func: callback
    });
  };

  // ### Stop listening for an event
  // Unregisters a callback as a listener for a given event.
  // The event will be removed automatically when the component is
  // destructed.

  // `void Component.removeEventListener(event, callback)`

  // - `event`: The name of the event.
  // - `callback`: The function that was previously registered.
  TANK.Component.prototype.removeEventListener = function (event, callback)
  {
    if (this._functions)
    {
      TANK.error("The component " + this.name + "tried to stop listening to " + event + "before it was instantiated.");
      return;
    }

    if (!this._constructed)
    {
      TANK.error("The component " + this.name + " tried to stop listening to " + event + " from `construct`, but should do so from `initialize` instead");
      return;
    }

    if (!callback || !callback.apply)
    {
      TANK.error("The component " + this.name + " tried to stop listening to " + event + " but did not supply a valid callback");
      return;
    }

    var listeners = this.space._events[event],
      i;
    if (!listeners)
    {
      return;
    }

    // Delete the listener from the map
    for (i = 0; i < this._listeners.length; ++i)
    {
      if (this._listeners[i].evt === event && this._listeners[i].func === callback)
      {
        this._listeners.splice(i, 1);
        break;
      }
    }

    for (i = 0; i < listeners.length; ++i)
    {
      if (listeners[i].self === this && listeners[i].func === callback)
      {
        listeners.splice(i, 1);
        break;
      }
    }
  };

}(this.TANK = this.TANK ||
{}));<commit_msg>Fix a bug that prevented multiple interfaces from working<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013 David Evans, Killian Koenig
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// A component defines a small module of functionality and is
// initialized by adding it to an `Entity` or `Space`.
(function (TANK)
{
  "use strict";

  // ### Component constructor
  // Shouldn't really ever be called outside the engine.
  // If you are trying to register a new type of component,
  // use `TANK.registerComponent()`. If you are trying to add
  // a component to an entity, use `Entity.addComponent()`.
  //
  // name - The name of the component type.
  TANK.Component = function (name)
  {
    // ### Name of the component
    // `const string Component.name`
    this.name = name;

    // ### Parent Entity
    // `const Entity Component.parent`
    this.parent = null;
    this._functions = {};
    this._includes = [];
    this._interfaces = [];
  };

  // ### Instantiate a component
  // Creates a component object from this blueprint.
  // Will rarely be necessary to call outside the engine, as
  // this is dealt with internally in the `addComponent()` methods.
  //
  // - `return`: The new component object.
  TANK.Component.prototype.clone = function ()
  {
    var c = {};

    // Default functions
    c.construct = function () {};
    c.initialize = function () {};
    c.destruct = function () {};

    // Add interfaces
    c.interfaces = {};
    var i;
    for (i = 0; i < this._interfaces.length; ++i)
    {
      c.interfaces[this._interfaces[i]] = true;
    }

    // Add functions
    for (i in this._functions)
    {
      if (this._functions.hasOwnProperty(i))
      {
        c[i] = this._functions[i];
      }
    }

    // Set properties
    c.name = this.name;
    c._constructed = false;
    c.addEventListener = this.addEventListener;
    c.removeEventListener = this.removeEventListener;

    c._listeners = [];

    return c;
  };

  // ### Require other components
  // Defines a list of components that are required by this one
  // and will be automatically added before this one is added.
  // Components can be given as a string of comma seperated values,
  // a list of strings, or some combination of the above.

  // `Component Component.requires()`

  // - `return`: A reference to itself.
  TANK.Component.prototype.requires = function ()
  {
    var i, j, arg;
    for (i = 0; i < arguments.length; ++i)
    {
      arg = arguments[i];
      arg = arg.replace(/\s/g, "");
      var includes = arg.split(",");

      for (j = 0; j < includes.length; ++j)
      {
        this._includes.push(includes[j]);
      }
    }

    return this;
  };

  // ### Interfaces
  // Defines a list of interfaces that this component implements.
  // Manager components often look for components that implement a particular interface.
  // Common interfaces include `Drawable2D`, and `Collidable`
  // Interfaces can be given as a string of comma seperated values,
  // a list of strings, or some combination of the above.

  // `Component Component.interfaces()`

  // - `return`: A reference to itself.
  TANK.Component.prototype.interfaces = function ()
  {
    var i, j, arg;
    for (i = 0; i < arguments.length; ++i)
    {
      arg = arguments[i];
      arg = arg.replace(/\s/g, "");
      var interfaces = arg.split(",");

      for (j = 0; j < interfaces.length; ++j)
      {
        this._interfaces.push(interfaces[j]);
      }
    }

    return this;
  };

  // ### Set constructor function
  // Defines a function to be called after the component has been instantiated.
  // This is commonly where you would define member fields with default values.

  // `Component Component.construct(func)`

  // - `func`: A function to call after instantiation.
  // - `return`: A reference to itself.
  TANK.Component.prototype.construct = function (func)
  {
    this._functions.construct = func;
    return this;
  };

  // ### Set init function
  // Defines a function to be called after the component has been constructed and added to an entity.
  // This is commonly where you would listen for events and define other functions (e.g., `this.Foo = function(){}`)

  // `Component Component.initialize(func)`

  // - `func`: A function to call upon initialization.
  // - `return`: A reference to itself.
  TANK.Component.prototype.initialize = function (func)
  {
    this._functions.initialize = func;
    return this;
  };

  // ### Set uninit function
  // Defines a function to be called after the component has been removed from an entity.

  // `Component Component.destruct(func)`

  // - `func`: A function to call upon uninitialization.
  // - `return`: A reference to itself.
  TANK.Component.prototype.destruct = function (func)
  {
    this._functions.destruct = func;
    return this;
  };

  // ### Listen for an event
  // Registers a callback as a listener for a given event.
  // The event will be removed automatically when the component is
  // destructed.

  // `void Component.addEventListener(event, callback)`

  // - `event`: The name of the event.
  // - `callback`: A function to call when the event is triggered.
  TANK.Component.prototype.addEventListener = function (event, callback)
  {
    if (this._functions)
    {
      TANK.error("The component " + this.name + "tried to listen to " + event + "before it was instantiated.");
      return;
    }

    if (!this._constructed)
    {
      TANK.error("The component " + this.name + " tried to listen to " + event + " from `construct`, but should do so from `initialize` instead");
      return;
    }

    if (!callback || !callback.apply)
    {
      TANK.error("The component " + this.name + " tried to listen to " + event + " but did not supply a valid callback");
      return;
    }

    var listeners = this.space._events[event];
    if (!listeners)
    {
      listeners = [];
      this.space._events[event] = listeners;
    }

    this._listeners.push(
    {
      evt: event,
      self: this,
      func: callback
    });

    listeners.push(
    {
      self: this,
      func: callback
    });
  };

  // ### Stop listening for an event
  // Unregisters a callback as a listener for a given event.
  // The event will be removed automatically when the component is
  // destructed.

  // `void Component.removeEventListener(event, callback)`

  // - `event`: The name of the event.
  // - `callback`: The function that was previously registered.
  TANK.Component.prototype.removeEventListener = function (event, callback)
  {
    if (this._functions)
    {
      TANK.error("The component " + this.name + "tried to stop listening to " + event + "before it was instantiated.");
      return;
    }

    if (!this._constructed)
    {
      TANK.error("The component " + this.name + " tried to stop listening to " + event + " from `construct`, but should do so from `initialize` instead");
      return;
    }

    if (!callback || !callback.apply)
    {
      TANK.error("The component " + this.name + " tried to stop listening to " + event + " but did not supply a valid callback");
      return;
    }

    var listeners = this.space._events[event],
      i;
    if (!listeners)
    {
      return;
    }

    // Delete the listener from the map
    for (i = 0; i < this._listeners.length; ++i)
    {
      if (this._listeners[i].evt === event && this._listeners[i].func === callback)
      {
        this._listeners.splice(i, 1);
        break;
      }
    }

    for (i = 0; i < listeners.length; ++i)
    {
      if (listeners[i].self === this && listeners[i].func === callback)
      {
        listeners.splice(i, 1);
        break;
      }
    }
  };

}(this.TANK = this.TANK ||
{}));<|endoftext|>


<commit_before>'use strict';

/**
 * build -- Initiates the Babel build process. A valid `npm-scripts.json` configuration file must be located in the
 * root path. This configuration file must contain a `build` hash with a `babel` hash entry that contains the
 * following options:
 * ```
 * (string)          source - The source directory.
 * (string)          destination - The destination directory.
 * (Array<string>)   options - An array of optional parameters which are appended to the invocation of Babel. Please
 *                             run `./node_modules/.bin/babel --help` for all available options.
 * ```
 */

var cp =                require('child_process');
var fs =                require('fs-extra');
var stripJsonComments = require('strip-json-comments');

// Verify that `npm-scripts.json` exists.
try
{
   if (!fs.statSync('./npm-scripts.json').isFile())
   {
      throw new Error("'npm-scripts.json' not found in root path.");
   }
}
catch(err)
{
   throw new Error("TyphonJS NPM script (build) error: " + err);
}

// Verify that `Babel` exists.
try
{
   if (!fs.statSync('./node_modules/.bin/babel').isFile())
   {
      throw new Error("could not locate Babel at './node_modules/.bin/babel'.");
   }
}
catch(err)
{
   throw new Error("TyphonJS NPM script (build) error: " + err);
}

// Load `npm-scripts.json` and strip comments.
var configInfo = JSON.parse(stripJsonComments(fs.readFileSync('./npm-scripts.json', 'utf-8')));

// Verify that `build` entry is an object.
if (typeof configInfo.build !== 'object')
{
   throw new Error(
    "TyphonJS NPM script (build) error: build entry is not an object or is missing in 'npm-scripts.json'.");
}

// Verify that `build.babel` entry is an object.
if (typeof configInfo.build.babel !== 'object')
{
   throw new Error(
    "TyphonJS NPM script (build) error: build.babel entry is not an object or is missing in 'npm-scripts.json'.");
}

var babelConfig = configInfo.build.babel;

// Verify that source entry is a string.
if (typeof babelConfig.source !== 'string')
{
   throw new Error(
    "TyphonJS NPM script (build) error: build.babel.source entry is not a string or is missing in 'npm-scripts.json'.");
}

// Verify that destination entry is a string.
if (typeof babelConfig.destination !== 'string')
{
   throw new Error(
    "TyphonJS NPM script (build) error: build.babel.destination entry is not a string or is missing in "
     + "'npm-scripts.json'.");
}

// Verify that source entry is a directory.
try
{
   if (!fs.statSync(babelConfig.source).isDirectory())
   {
      throw new Error("build.babel.source entry is not a directory: " + babelConfig.source);
   }
}
catch(err)
{
   throw new Error("TyphonJS NPM script (build) error: " + err);
}

// Create or empty destination directory.
fs.emptyDirSync(babelConfig.destination);

// Verify that destination entry is a directory.
try
{
   if (!fs.statSync(babelConfig.destination).isDirectory())
   {
      throw new Error("build.babel.destination entry is not a directory: " + babelConfig.destination);
   }
}
catch(err)
{
   throw new Error("TyphonJS NPM script (build) error: " + err);
}

// Build base execution command.
var exec = './node_modules/.bin/babel ' + babelConfig.source + ' -d ' + babelConfig.destination;

// Add any optional parameters.
if (typeof babelConfig.options !== 'undefined')
{
   if (!Array.isArray(babelConfig.options))
   {
      throw new Error(
       "TyphonJS NPM script (build) error: build.babel.options entry is not an array in 'npm-scripts.json'.");
   }

   exec += ' ' + babelConfig.options.join(' ');
}

// Notify what command is being executed then execute it.
process.stdout.write('Executing: ' + exec + '\n');
cp.execSync(exec, { stdio: 'inherit' });

// Verify that there are files / dirs in destination directory. If the directory is empty then fail.
var files = fs.readdirSync(babelConfig.destination);
if (files.length === 0)
{
   throw new Error(
    "TyphonJS NPM script (build) error: empty destination directory: " + babelConfig.destination);
}<commit_msg>0.0.4 release<commit_after>'use strict';

/**
 * build -- Initiates the Babel build process. A valid `npm-scripts.json` configuration file must be located in the
 * root path. This configuration file must contain a `build` hash with a `babel` hash entry that contains the
 * following options:
 * ```
 * (string)          source - The source directory.
 * (string)          destination - The destination directory.
 * (Array<string>)   options - An array of optional parameters which are appended to the invocation of Babel. Please
 *                             run `./node_modules/.bin/babel --help` for all available options.
 * ```
 */

var cp =                require('child_process');
var fs =                require('fs-extra');
var stripJsonComments = require('strip-json-comments');

// Verify that `npm-scripts.json` exists.
try
{
   if (!fs.statSync('./npm-scripts.json').isFile())
   {
      throw new Error("'npm-scripts.json' not found in root path.");
   }
}
catch(err)
{
   throw new Error("TyphonJS NPM script (build) error: " + err);
}

// Verify that `Babel` exists.
try
{
   if (!fs.statSync('./node_modules/.bin/babel').isFile())
   {
      throw new Error("could not locate Babel at './node_modules/.bin/babel'.");
   }
}
catch(err)
{
   throw new Error("TyphonJS NPM script (build) error: " + err);
}

// Load `npm-scripts.json` and strip comments.
var configInfo = JSON.parse(stripJsonComments(fs.readFileSync('./npm-scripts.json', 'utf-8')));

// Verify that `build` entry is an object.
if (typeof configInfo.build !== 'object')
{
   throw new Error(
    "TyphonJS NPM script (build) error: 'build' entry is not an object or is missing in 'npm-scripts.json'.");
}

// Verify that `build.babel` entry is an object.
if (typeof configInfo.build.babel !== 'object')
{
   throw new Error(
    "TyphonJS NPM script (build) error: 'build.babel' entry is not an object or is missing in 'npm-scripts.json'.");
}

var babelConfig = configInfo.build.babel;

// Verify that source entry is a string.
if (typeof babelConfig.source !== 'string')
{
   throw new Error(
    "TyphonJS NPM script (build) error: 'build.babel.source' entry is not a string or is missing in "
    + "'npm-scripts.json'.");
}

// Verify that destination entry is a string.
if (typeof babelConfig.destination !== 'string')
{
   throw new Error(
    "TyphonJS NPM script (build) error: 'build.babel.destination' entry is not a string or is missing in "
     + "'npm-scripts.json'.");
}

// Verify that source entry is a directory.
try
{
   if (!fs.statSync(babelConfig.source).isDirectory())
   {
      throw new Error("'build.babel.source' entry is not a directory: " + babelConfig.source);
   }
}
catch(err)
{
   throw new Error("TyphonJS NPM script (build) error: " + err);
}

// Create or empty destination directory.
fs.emptyDirSync(babelConfig.destination);

// Verify that destination entry is a directory.
try
{
   if (!fs.statSync(babelConfig.destination).isDirectory())
   {
      throw new Error("'build.babel.destination' entry is not a directory: " + babelConfig.destination);
   }
}
catch(err)
{
   throw new Error("TyphonJS NPM script (build) error: " + err);
}

// Build base execution command.
var exec = './node_modules/.bin/babel ' + babelConfig.source + ' -d ' + babelConfig.destination;

// Add any optional parameters.
if (typeof babelConfig.options !== 'undefined')
{
   if (!Array.isArray(babelConfig.options))
   {
      throw new Error(
       "TyphonJS NPM script (build) error: 'build.babel.options' entry is not an array in 'npm-scripts.json'.");
   }

   exec += ' ' + babelConfig.options.join(' ');
}

// Notify what command is being executed then execute it.
process.stdout.write('Executing: ' + exec + '\n');
cp.execSync(exec, { stdio: 'inherit' });

// Verify that there are files / dirs in destination directory. If the directory is empty then fail.
var files = fs.readdirSync(babelConfig.destination);
if (files.length === 0)
{
   throw new Error(
    "TyphonJS NPM script (build) error: empty destination directory: " + babelConfig.destination);
}<|endoftext|>


<commit_before>"use strict";

var async = require('async');
function RestWrap(opts) {

  /**
  * Rest module
  */

  function Rest(options) {
    options = options || {};

    this.encoding = options.encoding || 'utf8';
    this.protocol = options.protocol || 'http';
    this.middleware = [];
    this._requestModule = require(this.protocol);

    if (options.middleware) this.middleware = this.middleware.concat(options.middleware); //In case we want default middleware, concat instead of replacing this.middleware
  }

  Rest.middleware = require('./middleware');

  /**
  * Generic REST invocation function
  * Should handle all verbs
  * @param	  {object}	  opts		  options for request
  * @param  	{string}	  body		  body of request
  * @param  	{Function}	callback	function(data) error handling & parseing should be done by module
  */
  Rest.prototype.request = function(opts, body, callback) {
    var self = this,
        middleware = this.middleware,
        callbackArgs = [],
        isDone = false,
        req;

    if (typeof body === 'function') {
      callback = body;
      body = '';
    }

    function finish(err, res) {
      if (isDone) return; //This would only happen if an error occurs AFTER the res has ended...doubtful that would ever happen.

      if (err) {
        callbackArgs[0] = err; //If there's an error and no res, collect it and keep waiting until the 'end' event
      } else {
        isDone = true;
        callbackArgs[1] = res;

        if (!callbackArgs[0] && middleware.length) { //If there isn't already an error and there is middleware, run them
          async.eachSeries(
            middleware,
            function(middlewareItem, fn) {
              middlewareItem(res, fn);
            },
            function(err) {
              if (err) callbackArgs[0] = err;
              callback.apply(null, callbackArgs); //Pass both the err and res to the callback, because often times the body will be just fine despite errors
            }
          );
        } else {
          callback.apply(null, callbackArgs); //Pass both the err and res to the callback, because often times the body will be just fine despite errors
        }
      }
    }

    if (typeof body !== 'string') body = JSON.stringify(body);

    //TODO: add headers like encoding
    req = this._requestModule.request(opts, function(res) {
      var data = '';

      res.setEncoding(self.encoding);

      res.on('data', function(d){data+=d;}); //capture data
      res.on('end', function() {
        res.body = res.message = data;
        finish(null, res);
      });
    });

    req.on('error', finish);

    req.write(body);
    req.end();
  };

  return new Rest(opts);
}

module.exports = RestWrap;
<commit_msg>fixed paths<commit_after>"use strict";

var async = require('async');
function RestWrap(opts) {

  /**
  * Rest module
  */

  function Rest(options) {
    options = options || {};

    this.encoding = options.encoding || 'utf8';
    this.protocol = options.protocol || 'http';
    this.middleware = [];
    this._requestModule = require(this.protocol);

    if (options.middleware) this.middleware = this.middleware.concat(options.middleware); //In case we want default middleware, concat instead of replacing this.middleware
  }

  Rest.middleware = require('../middleware');

  /**
  * Generic REST invocation function
  * Should handle all verbs
  * @param	  {object}	  opts		  options for request
  * @param  	{string}	  body		  body of request
  * @param  	{Function}	callback	function(data) error handling & parseing should be done by module
  */
  Rest.prototype.request = function(opts, body, callback) {
    var self = this,
        middleware = this.middleware,
        callbackArgs = [],
        isDone = false,
        req;

    if (typeof body === 'function') {
      callback = body;
      body = '';
    }

    function finish(err, res) {
      if (isDone) return; //This would only happen if an error occurs AFTER the res has ended...doubtful that would ever happen.

      if (err) {
        callbackArgs[0] = err; //If there's an error and no res, collect it and keep waiting until the 'end' event
      } else {
        isDone = true;
        callbackArgs[1] = res;

        if (!callbackArgs[0] && middleware.length) { //If there isn't already an error and there is middleware, run them
          async.eachSeries(
            middleware,
            function(middlewareItem, fn) {
              middlewareItem(res, fn);
            },
            function(err) {
              if (err) callbackArgs[0] = err;
              callback.apply(null, callbackArgs); //Pass both the err and res to the callback, because often times the body will be just fine despite errors
            }
          );
        } else {
          callback.apply(null, callbackArgs); //Pass both the err and res to the callback, because often times the body will be just fine despite errors
        }
      }
    }

    if (typeof body !== 'string') body = JSON.stringify(body);

    //TODO: add headers like encoding
    req = this._requestModule.request(opts, function(res) {
      var data = '';

      res.setEncoding(self.encoding);

      res.on('data', function(d){data+=d;}); //capture data
      res.on('end', function() {
        res.body = res.message = data;
        finish(null, res);
      });
    });

    req.on('error', finish);

    req.write(body);
    req.end();
  };

  return new Rest(opts);
}

module.exports = RestWrap;
<|endoftext|>


<commit_before>//  elmoSlider v0.0.4 by Michal Dolny
//  dependencies: jQuery

(function($){

    $.fn.elmoSlider = function(options){
        var defaults = {
            speed: 500,
            pause: 1000,
            anim: 'slide',
            slideWidth: $(this).find('.slider-content').children().width(),
            autoPlay: false
        };
        options = $.extend(defaults, options);

        var state = [0];
        var i = 0;
        var j = 0;
        var slider = $(this);

        slider.find('.slide').each(function(){
            state[j]=-(j * $(this).width());
            j++;
        });

        if(options.autoPlay === false){
            $(slider).on('click', '.slide-left', function(){
                i--;
                if( i < 0 ){ i = 2 };
                if(options.anim == 'opacity'){
                    $('.active-slide').removeClass('active-slide');
                    $('.slide'+(i+1)).addClass('active-slide');
                }else{
                    $(slider).find('.slider-content').animate({'margin-left': state[i]}, options.speed);
                };
            });
            $(slider).on('click', '.slide-right', function(){
                i++;
                if( i > 2 ){ i = 0 };
                if(options.anim == 'opacity'){
                    $('.active-slide').removeClass('active-slide');
                    $('.slide'+(i+1)).addClass('active-slide');
                }else{
                    $(slider).find('.slider-content').animate({'margin-left': state[i]}, options.speed);
                };
            });
        }

    };

})(jQuery);
<commit_msg>set the width of the slider-wrap<commit_after>//  elmoSlider v0.0.4 by Michal Dolny
//  dependencies: jQuery

(function($){

    $.fn.elmoSlider = function(options){
        var defaults = {
            speed: 500,
            pause: 1000,
            anim: 'slide',
            slideWidth: $(this).find('.slider-content').children().width(),
            autoPlay: false
        };
        options = $.extend(defaults, options);

        var slider = $(this);
        var state = [0];
        var i = 0;
        var j = 0;

        slider.find('.slide').each(function(){
            state[j]=-(j * $(this).width());
            j++;
        });

        slider.find('.slider-wrap').addBack('.slider-wrap').css({
            width: options.slideWidth
        })

        if(options.autoPlay === false){
            $(slider).on('click', '.slide-left', function(){
                i--;
                if( i < 0 ){ i = 2 };
                if(options.anim == 'opacity'){
                    $('.active-slide').removeClass('active-slide');
                    $('.slide'+(i+1)).addClass('active-slide');
                }else{
                    $(slider).find('.slider-content').animate({'margin-left': state[i]}, options.speed);
                };
            });
            $(slider).on('click', '.slide-right', function(){
                i++;
                if( i > 2 ){ i = 0 };
                if(options.anim == 'opacity'){
                    $('.active-slide').removeClass('active-slide');
                    $('.slide'+(i+1)).addClass('active-slide');
                }else{
                    $(slider).find('.slider-content').animate({'margin-left': state[i]}, options.speed);
                };
            });
        }

    };

})(jQuery);
<|endoftext|>


<commit_before>// Copyright 2017-2019, University of Colorado Boulder

/**
 * IO type for Emitter.
 *
 * Providing validators to instrumented Emitters:
 * Instrumented Emitters should have their `validators` for each argument passed via EmitterIO (the phetioType).
 * To provide validators, there are two methods. First, by default each TypeIO has its own
 * validator that will be used. So specifying an argument object like `{ type: NumberIO }` will automatically use
 * `NumberIO.validator` as the validator. This can be overridden with the `validator` key (second option), like
 * { type: NumberIO, validator: { isValidValue: v=> typeof v === 'number' &&  v < 5 } }`
 * NOTE: currently the implementation is either/or, if a validator is provided via the `validator` key, the validator
 * from the `type` will be ignored.
 * see https://github.com/phetsims/axon/issues/204 for more details.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 * @author Andrew Adare (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var ActionIO = require( 'AXON/ActionIO' );
  var axon = require( 'AXON/axon' );
  var FunctionIO = require( 'TANDEM/types/FunctionIO' );
  var phetioInherit = require( 'TANDEM/phetioInherit' );
  var VoidIO = require( 'TANDEM/types/VoidIO' );

  /**
   * IO type for Emitter
   * Emitter for 0, 1 or 2 args, or maybe 3.
   *
   * @param {Object[]} argumentObjects, each with {name:string, type: IO type, documentation: string}
   * @returns {EmitterIOImpl} - the parameterized type
   * @constructor
   */
  function EmitterIO( argumentObjects ) {

    var elementTypes = argumentObjects.map( argumentObject => argumentObject.type );
    // var validators = argumentObjects.map( argumentObject => argumentObject.validator || argumentObject.type.validator );

    const ActionIOImpl = ActionIO( argumentObjects );

    /**
     * @param {Emitter} emitter
     * @param {string} phetioID
     * @constructor
     */
    var EmitterIOImpl = function EmitterIOImpl( emitter, phetioID ) {
      assert && assert( argumentObjects, 'phetioArgumentTypes should be defined' );

      ActionIOImpl.call( this, emitter, phetioID );
    };

    return phetioInherit( ActionIOImpl, 'EmitterIO', EmitterIOImpl, {
      addListener: {
        returnType: VoidIO,
        parameterTypes: [ FunctionIO( VoidIO, elementTypes ) ],
        implementation: function( listener ) {
          this.phetioObject.addListener( listener );
        },
        documentation: 'Adds a listener which will be called when the emitter emits.'
      },
      emit: {
        returnType: VoidIO,
        parameterTypes: elementTypes,

        // Match `Emitter.emit`'s dynamic number of arguments
        implementation: function() {
          this.phetioObject.emit.apply( this.phetioObject, arguments );
        },
        documentation: 'Emits a single event to all listeners.',
        invocableForReadOnlyElements: false
      }
    }, {
      documentation: 'Emits when an event occurs. ' + ( argumentObjects.length === 0 ? 'No arguments.' : 'The arguments are:
' +
                     '
' + argumentObjects.map( function( element ) {
          const docText = element.documentation ? '. ' + element.documentation : '';
          return '
' + element.name + ': ' + element.type.typeName + docText + '
';
        } ).join( '\n' ) + '
' ),
      validator: {
        isValidValue: v => {
          // constants
          var Emitter = window.phet ? phet.axon.Emitter : axon.Emitter;
          return v instanceof Emitter;
        }
      }
    } );
  }

  axon.register( 'EmitterIO', EmitterIO );

  return EmitterIO;
} );

<commit_msg>Restore subtype statics, see https://github.com/phetsims/axon/issues/241<commit_after>// Copyright 2017-2019, University of Colorado Boulder

/**
 * IO type for Emitter.
 *
 * Providing validators to instrumented Emitters:
 * Instrumented Emitters should have their `validators` for each argument passed via EmitterIO (the phetioType).
 * To provide validators, there are two methods. First, by default each TypeIO has its own
 * validator that will be used. So specifying an argument object like `{ type: NumberIO }` will automatically use
 * `NumberIO.validator` as the validator. This can be overridden with the `validator` key (second option), like
 * { type: NumberIO, validator: { isValidValue: v=> typeof v === 'number' &&  v < 5 } }`
 * NOTE: currently the implementation is either/or, if a validator is provided via the `validator` key, the validator
 * from the `type` will be ignored.
 * see https://github.com/phetsims/axon/issues/204 for more details.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 * @author Andrew Adare (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var ActionIO = require( 'AXON/ActionIO' );
  var axon = require( 'AXON/axon' );
  var FunctionIO = require( 'TANDEM/types/FunctionIO' );
  var phetioInherit = require( 'TANDEM/phetioInherit' );
  var VoidIO = require( 'TANDEM/types/VoidIO' );

  /**
   * IO type for Emitter
   * Emitter for 0, 1 or 2 args, or maybe 3.
   *
   * @param {Object[]} argumentObjects, each with {name:string, type: IO type, documentation: string}
   * @returns {EmitterIOImpl} - the parameterized type
   * @constructor
   */
  function EmitterIO( argumentObjects ) {

    var elementTypes = argumentObjects.map( argumentObject => argumentObject.type );
    var validators = argumentObjects.map( argumentObject => argumentObject.validator || argumentObject.type.validator );

    const ActionIOImpl = ActionIO( argumentObjects );

    /**
     * @param {Emitter} emitter
     * @param {string} phetioID
     * @constructor
     */
    var EmitterIOImpl = function EmitterIOImpl( emitter, phetioID ) {
      assert && assert( argumentObjects, 'phetioArgumentTypes should be defined' );

      ActionIOImpl.call( this, emitter, phetioID );
    };

    return phetioInherit( ActionIOImpl, 'EmitterIO', EmitterIOImpl, {
      addListener: {
        returnType: VoidIO,
        parameterTypes: [ FunctionIO( VoidIO, elementTypes ) ],
        implementation: function( listener ) {
          this.phetioObject.addListener( listener );
        },
        documentation: 'Adds a listener which will be called when the emitter emits.'
      },
      emit: {
        returnType: VoidIO,
        parameterTypes: elementTypes,

        // Match `Emitter.emit`'s dynamic number of arguments
        implementation: function() {
          this.phetioObject.emit.apply( this.phetioObject, arguments );
        },
        documentation: 'Emits a single event to all listeners.',
        invocableForReadOnlyElements: false
      }
    }, {
      documentation: 'Emits when an event occurs. ' + ( argumentObjects.length === 0 ? 'No arguments.' : 'The arguments are:
' +
                     '
' + argumentObjects.map( function( element ) {
          const docText = element.documentation ? '. ' + element.documentation : '';
          return '
' + element.name + ': ' + element.type.typeName + docText + '
';
        } ).join( '\n' ) + '
' ),

      /**
       * {Array.<ObjectIO>} - typeIOs
       */
      parameterTypes: elementTypes,

      /**
       * {Array.<Object>} - see constructor for details on object literal keys
       */
      elements: argumentObjects,

      /**
       * A list of validators, one for each argument that will be emitted.
       * {ValidatorDef[]}
       */
      validators: validators,  // TODO: https://github.com/phetsims/axon/issues/241 Can this be supplied by the parent?

      validator: {
        isValidValue: v => {
          // constants
          var Emitter = window.phet ? phet.axon.Emitter : axon.Emitter;
          return v instanceof Emitter;
        }
      }
    } );
  }

  axon.register( 'EmitterIO', EmitterIO );

  return EmitterIO;
} );<|endoftext|>


<commit_before>// First, ensure the build is running in production mode
process.env.NODE_ENV = 'production';

const webpack = require('webpack');
const webpackConfig = require('../config/webpack/webpack.config');

webpack(webpackConfig, (err, stats) => {
  if (err) {
    console.log(err);
  } else {
    console.log('Build complete!');
  }
});
<commit_msg>Log webpack stats on build completion<commit_after>// First, ensure the build is running in production mode
process.env.NODE_ENV = 'production';

const webpack = require('webpack');
const webpackConfig = require('../config/webpack/webpack.config');

webpack(webpackConfig, (err, stats) => {
  if (err) {
    console.log(err);
  } else {
    console.log(stats.toString({
      chunks: false, // Makes the build much quieter
      colors: true
    }));
  }
});
<|endoftext|>


<commit_before>/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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.
 */

define(function(require, exports, module) {
var examiner = exports;

var Promise = require('util/promise');
var util = require('util/util');
var assert = require('test/assert');
var stati = require('test/status').stati;

/**
 * Test harness data
 */
examiner.suites = {};
examiner.setups = [];
examiner.shutdowns = [];

/**
 * The gap between tests when running async
 */
var delay = 10;

/**
 * Add a test suite. Generally used like:
 * test.addSuite('foo', require('path/to/foo'));
 */
examiner.addSuite = function(name, suite) {
  examiner.suites[name] = new Suite(name, suite);
};

/**
 * Run all the tests asynchronously
 */
examiner.run = function(options, callback) {
  var onComplete = function() {
    if (typeof callback === 'function') {
      callback.call();
    }
  };

  var doneSetup = callAll(examiner.setups, [ options ]);

  var doneTests = doneSetup.then(function() {
    var suiteNames = Object.keys(examiner.suites);
    return util.promiseEach(suiteNames, function(suiteName) {
      return examiner.suites[suiteName].run(options);
    }).then(onComplete);
  });

  return doneTests.then(function() {
    return callAll(examiner.shutdowns, [ options ]);
  });
};

function callAll(funcs, args) {
  return util.promiseEach(funcs, function(func) {
    func.apply(null, args);
  });
}

/**
 * Create a JSON object suitable for serialization
 */
examiner.toRemote = function() {
  return {
    suites: Object.keys(examiner.suites).map(function(suiteName) {
      return examiner.suites[suiteName].toRemote();
    }),
    summary: {
      checks: this.checks,
      status: this.status
    }
  };
};

/**
 * Reset all the tests to their original state
 */
examiner.reset = function() {
  Object.keys(examiner.suites).forEach(function(suiteName) {
    examiner.suites[suiteName].reset();
  }, this);
};

/**
 * The number of checks in this set of test suites is the sum of the checks in
 * the test suites.
 */
Object.defineProperty(examiner, 'checks', {
  get: function() {
    return  Object.keys(examiner.suites).reduce(function(current, suiteName) {
      return current + examiner.suites[suiteName].checks;
    }, 0);
  },
  enumerable: true
});

/**
 * The status of this set of test suites is the worst of the statuses of the
 * contained test suites.
 */
Object.defineProperty(examiner, 'status', {
  get: function() {
    return Object.keys(examiner.suites).reduce(function(status, suiteName) {
      var suiteStatus = examiner.suites[suiteName].status;
      return status.index > suiteStatus.index ? status : suiteStatus;
    }, stati.notrun);
  },
  enumerable: true
});

/**
 * Output a test summary to console.log
 */
examiner.detailedResultLog = function() {
  Object.keys(this.suites).forEach(function(suiteName) {
    var suite = examiner.suites[suiteName];

    console.log(suite.name + ': ' + suite.status.name +
            ' (funcs=' + Object.keys(suite.tests).length +
            ', checks=' + suite.checks +
            ', skipped=' + suite.skippedCount + ')');

    Object.keys(suite.tests).forEach(function(testName) {
      var test = suite.tests[testName];
      if (test.status !== stati.pass) {
        console.log('- ' + test.name + ': ' + test.status.name);
        test.failures.forEach(function(failure) {
          console.log('  - ' + failure.message);
          if (failure.params) {
            console.log('    - P1: ' + failure.p1);
            console.log('    - P2: ' + failure.p2);
          }
        }, this);
      }
    }, this);
  }, this);

  console.log('');
  console.log('Summary: ' + this.status.name + ' (' + this.checks + ' checks)');
};

/**
 * A suite is a group of tests
 */
function Suite(suiteName, suite) {
  this.name = suiteName.replace(/gclitest\//, '');
  this.suite = suite;

  this.tests = {};

  Object.keys(suite).forEach(function(testName) {
    if (testName !== 'setup' && testName !== 'shutdown') {
      var test = new Test(this, testName, suite[testName]);
      this.tests[testName] = test;
    }
  }, this);
}

/**
 * Reset all the tests to their original state
 */
Suite.prototype.reset = function() {
  Object.keys(this.tests).forEach(function(testName) {
    this.tests[testName].reset();
  }, this);
};

/**
 * Run all the tests in this suite asynchronously (includes doing a setup and
 * shutdown of the suite)
 */
Suite.prototype.run = function(options) {
  var doneSetup = callAll(examiner.setups, [ options ]);

  var doneTests = doneSetup.then(function() {
    return this._run(options);
  }.bind(this));

  return doneTests.then(function() {
    return callAll(examiner.shutdowns, [ options ]);
  }.bind(this));
};

/**
 * Internal function to run all the tests in this suite asynchronously without
 * any setup/shutdown at the suite level. i.e. this function is designed to be
 * called by examiner.run or suite.run.
 */
Suite.prototype._run = function(options) {
  this._setup(options);

  var onComplete = function() {
    this._shutdown(options);
  }.bind(this);

  var testNames = Object.keys(this.tests);
  return util.promiseEach(testNames, function(testName) {
    return this.tests[testName].run(options);
  }.bind(this)).then(onComplete);
};

/**
 * Create a JSON object suitable for serialization
 */
Suite.prototype.toRemote = function() {
  return {
    name: this.name,
    tests: Object.keys(this.tests).map(function(testName) {
      return this.tests[testName].toRemote();
    }.bind(this))
  };
};

/**
 * The number of checks in this suite is the sum of the checks in the contained
 * tests.
 */
Object.defineProperty(Suite.prototype, 'checks', {
  get: function() {
    return Object.keys(this.tests).reduce(function(prevChecks, testName) {
      return prevChecks + this.tests[testName].checks;
    }.bind(this), 0);
  },
  enumerable: true
});

/**
 * The status of a test suite is the worst of the statuses of the contained
 * tests.
 */
Object.defineProperty(Suite.prototype, 'status', {
  get: function() {
    return Object.keys(this.tests).reduce(function(prevStatus, testName) {
      var suiteStatus = this.tests[testName].status;
      return prevStatus.index > suiteStatus.index ? prevStatus : suiteStatus;
    }.bind(this), stati.notrun);
  },
  enumerable: true
});

/**
 * The status of a test suite is the worst of the statuses of the contained
 * tests.
 */
Object.defineProperty(Suite.prototype, 'skippedCount', {
  get: function() {
    return Object.keys(this.tests).reduce(function(count, testName) {
      return count + this.tests[testName].skipped.length;
    }.bind(this), 0);
  },
  enumerable: true
});

/**
 * Defensively setup the test suite
 */
Suite.prototype._setup = function(options) {
  if (typeof this.suite.setup !== 'function') {
    return true;
  }

  try {
    this.suite.setup(options);
    return true;
  }
  catch (ex) {
    this._logToAllTests('' + ex);
    console.error(ex);
    if (ex.stack) {
      console.error(ex.stack);
    }
    return false;
  }
};

/**
 * Defensively shutdown the test suite
 */
Suite.prototype._shutdown = function(options) {
  if (typeof this.suite.shutdown !== 'function') {
    return true;
  }

  try {
    this.suite.shutdown(options);
    return true;
  }
  catch (ex) {
    this._logToAllTests('' + ex);
    console.error(ex);
    if (ex.stack) {
      console.error(ex.stack);
    }
    return false;
  }
};

/**
 * Something has gone wrong that affects all tests in this Suite
 */
Suite.prototype._logToAllTests = function(message) {
  var priorCurrentTest = assert.currentTest;
  Object.keys(this.tests).forEach(function(testName) {
    assert.currentTest = this.tests[testName];
    assert.ok(false, message);
  }.bind(this));
  assert.currentTest = priorCurrentTest;
};

/**
 * A test represents data about a single test function
 */
function Test(suite, name, func) {
  this.suite = suite;
  this.name = name;
  this.func = func;
  this.title = name.replace(/^test/, '').replace(/([A-Z])/g, ' $1');

  this.failures = [];
  this.skipped = [];
  this.status = stati.notrun;
  this.checks = 0;
}

Test.prototype.toString = function() {
  return this.suite.name + '.' + this.name;
};

/**
 * Reset the test to its original state
 */
Test.prototype.reset = function() {
  this.failures = [];
  this.skipped = [];
  this.status = stati.notrun;
  this.checks = 0;
};

/**
 * Run all the tests in this suite asynchronously
 */
Test.prototype.run = function(options) {
  assert.currentTest = this;
  this.status = stati.executing;

  if (assert.testLogging) {
    console.log('START: ' + assert.currentTest);
  }

  var deferred = Promise.defer();
  setTimeout(function() {
    try {
      var reply = this.func.apply(this.suite, [ options ]);
      deferred.resolve(reply);
    }
    catch (ex) {
      deferred.reject(ex);
    }
  }.bind(this), delay);

  var onDone = function() {
    if (this.status === stati.executing) {
      this.status = this.failures.length === 0 ? stati.pass : stati.fail;
    }

    if (assert.testLogging) {
      console.log('END: ' + assert.currentTest);
    }

    assert.currentTest = null;
  }.bind(this);

  var onError = function(ex) {
    assert.ok(false, '' + ex);
    console.error(ex.stack);
    onDone();
  };

  return deferred.promise.then(onDone, onError);
};

/**
 * Create a JSON object suitable for serialization
 */
Test.prototype.toRemote = function() {
  return {
    name: this.name,
    title: this.title,
    status: this.status,
    failures: this.failures,
    checks: this.checks
  };
};

});
<commit_msg>asynctypes-685526: Protect examiner.suites<commit_after>/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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.
 */

define(function(require, exports, module) {
var examiner = exports;

var Promise = require('util/promise');
var util = require('util/util');
var assert = require('test/assert');
var stati = require('test/status').stati;

/**
 * Test harness data
 */
examiner._suites = {};
examiner.setups = [];
examiner.shutdowns = [];

/**
 * The gap between tests when running async
 */
var delay = 10;

/**
 * Add a test suite. Generally used like:
 * test.addSuite('foo', require('path/to/foo'));
 */
examiner.addSuite = function(name, suite) {
  examiner._suites[name] = new Suite(name, suite);
};

/**
 * Provide a clone of examiner._suites for external consumption
 */
Object.defineProperty(examiner, 'suites', {
  value: function() {
    var reply = {};
    Object.keys(examiner._suites).forEach(function(name) {
      reply[name] = examiner._suites[name];
    });
    return reply;
  },
  enumerable: true
});

/**
 * Run all the tests asynchronously
 */
examiner.run = function(options, callback) {
  var onComplete = function() {
    if (typeof callback === 'function') {
      callback.call();
    }
  };

  var doneSetup = callAll(examiner.setups, [ options ]);

  var doneTests = doneSetup.then(function() {
    var suiteNames = Object.keys(examiner._suites);
    return util.promiseEach(suiteNames, function(suiteName) {
      return examiner._suites[suiteName].run(options);
    }).then(onComplete);
  });

  return doneTests.then(function() {
    return callAll(examiner.shutdowns, [ options ]);
  });
};

function callAll(funcs, args) {
  return util.promiseEach(funcs, function(func) {
    func.apply(null, args);
  });
}

/**
 * Create a JSON object suitable for serialization
 */
examiner.toRemote = function() {
  return {
    suites: Object.keys(examiner._suites).map(function(suiteName) {
      return examiner._suites[suiteName].toRemote();
    }),
    summary: {
      checks: this.checks,
      status: this.status
    }
  };
};

/**
 * Reset all the tests to their original state
 */
examiner.reset = function() {
  Object.keys(examiner._suites).forEach(function(suiteName) {
    examiner._suites[suiteName].reset();
  }, this);
};

/**
 * The number of checks in this set of test suites is the sum of the checks in
 * the test suites.
 */
Object.defineProperty(examiner, 'checks', {
  get: function() {
    return  Object.keys(examiner._suites).reduce(function(current, suiteName) {
      return current + examiner._suites[suiteName].checks;
    }, 0);
  },
  enumerable: true
});

/**
 * The status of this set of test suites is the worst of the statuses of the
 * contained test suites.
 */
Object.defineProperty(examiner, 'status', {
  get: function() {
    return Object.keys(examiner._suites).reduce(function(status, suiteName) {
      var suiteStatus = examiner._suites[suiteName].status;
      return status.index > suiteStatus.index ? status : suiteStatus;
    }, stati.notrun);
  },
  enumerable: true
});

/**
 * Output a test summary to console.log
 */
examiner.detailedResultLog = function() {
  Object.keys(this.suites).forEach(function(suiteName) {
    var suite = examiner._suites[suiteName];

    console.log(suite.name + ': ' + suite.status.name +
            ' (funcs=' + Object.keys(suite.tests).length +
            ', checks=' + suite.checks +
            ', skipped=' + suite.skippedCount + ')');

    Object.keys(suite.tests).forEach(function(testName) {
      var test = suite.tests[testName];
      if (test.status !== stati.pass) {
        console.log('- ' + test.name + ': ' + test.status.name);
        test.failures.forEach(function(failure) {
          console.log('  - ' + failure.message);
          if (failure.params) {
            console.log('    - P1: ' + failure.p1);
            console.log('    - P2: ' + failure.p2);
          }
        }, this);
      }
    }, this);
  }, this);

  console.log('');
  console.log('Summary: ' + this.status.name + ' (' + this.checks + ' checks)');
};

/**
 * A suite is a group of tests
 */
function Suite(suiteName, suite) {
  this.name = suiteName.replace(/gclitest\//, '');
  this.suite = suite;

  this.tests = {};

  Object.keys(suite).forEach(function(testName) {
    if (testName !== 'setup' && testName !== 'shutdown') {
      var test = new Test(this, testName, suite[testName]);
      this.tests[testName] = test;
    }
  }, this);
}

/**
 * Reset all the tests to their original state
 */
Suite.prototype.reset = function() {
  Object.keys(this.tests).forEach(function(testName) {
    this.tests[testName].reset();
  }, this);
};

/**
 * Run all the tests in this suite asynchronously (includes doing a setup and
 * shutdown of the suite)
 */
Suite.prototype.run = function(options) {
  var doneSetup = callAll(examiner.setups, [ options ]);

  var doneTests = doneSetup.then(function() {
    return this._run(options);
  }.bind(this));

  return doneTests.then(function() {
    return callAll(examiner.shutdowns, [ options ]);
  }.bind(this));
};

/**
 * Internal function to run all the tests in this suite asynchronously without
 * any setup/shutdown at the suite level. i.e. this function is designed to be
 * called by examiner.run or suite.run.
 */
Suite.prototype._run = function(options) {
  this._setup(options);

  var onComplete = function() {
    this._shutdown(options);
  }.bind(this);

  var testNames = Object.keys(this.tests);
  return util.promiseEach(testNames, function(testName) {
    return this.tests[testName].run(options);
  }.bind(this)).then(onComplete);
};

/**
 * Create a JSON object suitable for serialization
 */
Suite.prototype.toRemote = function() {
  return {
    name: this.name,
    tests: Object.keys(this.tests).map(function(testName) {
      return this.tests[testName].toRemote();
    }.bind(this))
  };
};

/**
 * The number of checks in this suite is the sum of the checks in the contained
 * tests.
 */
Object.defineProperty(Suite.prototype, 'checks', {
  get: function() {
    return Object.keys(this.tests).reduce(function(prevChecks, testName) {
      return prevChecks + this.tests[testName].checks;
    }.bind(this), 0);
  },
  enumerable: true
});

/**
 * The status of a test suite is the worst of the statuses of the contained
 * tests.
 */
Object.defineProperty(Suite.prototype, 'status', {
  get: function() {
    return Object.keys(this.tests).reduce(function(prevStatus, testName) {
      var suiteStatus = this.tests[testName].status;
      return prevStatus.index > suiteStatus.index ? prevStatus : suiteStatus;
    }.bind(this), stati.notrun);
  },
  enumerable: true
});

/**
 * The status of a test suite is the worst of the statuses of the contained
 * tests.
 */
Object.defineProperty(Suite.prototype, 'skippedCount', {
  get: function() {
    return Object.keys(this.tests).reduce(function(count, testName) {
      return count + this.tests[testName].skipped.length;
    }.bind(this), 0);
  },
  enumerable: true
});

/**
 * Defensively setup the test suite
 */
Suite.prototype._setup = function(options) {
  if (typeof this.suite.setup !== 'function') {
    return true;
  }

  try {
    this.suite.setup(options);
    return true;
  }
  catch (ex) {
    this._logToAllTests('' + ex);
    console.error(ex);
    if (ex.stack) {
      console.error(ex.stack);
    }
    return false;
  }
};

/**
 * Defensively shutdown the test suite
 */
Suite.prototype._shutdown = function(options) {
  if (typeof this.suite.shutdown !== 'function') {
    return true;
  }

  try {
    this.suite.shutdown(options);
    return true;
  }
  catch (ex) {
    this._logToAllTests('' + ex);
    console.error(ex);
    if (ex.stack) {
      console.error(ex.stack);
    }
    return false;
  }
};

/**
 * Something has gone wrong that affects all tests in this Suite
 */
Suite.prototype._logToAllTests = function(message) {
  var priorCurrentTest = assert.currentTest;
  Object.keys(this.tests).forEach(function(testName) {
    assert.currentTest = this.tests[testName];
    assert.ok(false, message);
  }.bind(this));
  assert.currentTest = priorCurrentTest;
};

/**
 * A test represents data about a single test function
 */
function Test(suite, name, func) {
  this.suite = suite;
  this.name = name;
  this.func = func;
  this.title = name.replace(/^test/, '').replace(/([A-Z])/g, ' $1');

  this.failures = [];
  this.skipped = [];
  this.status = stati.notrun;
  this.checks = 0;
}

Test.prototype.toString = function() {
  return this.suite.name + '.' + this.name;
};

/**
 * Reset the test to its original state
 */
Test.prototype.reset = function() {
  this.failures = [];
  this.skipped = [];
  this.status = stati.notrun;
  this.checks = 0;
};

/**
 * Run all the tests in this suite asynchronously
 */
Test.prototype.run = function(options) {
  assert.currentTest = this;
  this.status = stati.executing;

  if (assert.testLogging) {
    console.log('START: ' + assert.currentTest);
  }

  var deferred = Promise.defer();
  setTimeout(function() {
    try {
      var reply = this.func.apply(this.suite, [ options ]);
      deferred.resolve(reply);
    }
    catch (ex) {
      deferred.reject(ex);
    }
  }.bind(this), delay);

  var onDone = function() {
    if (this.status === stati.executing) {
      this.status = this.failures.length === 0 ? stati.pass : stati.fail;
    }

    if (assert.testLogging) {
      console.log('END: ' + assert.currentTest);
    }

    assert.currentTest = null;
  }.bind(this);

  var onError = function(ex) {
    assert.ok(false, '' + ex);
    console.error(ex.stack);
    onDone();
  };

  return deferred.promise.then(onDone, onError);
};

/**
 * Create a JSON object suitable for serialization
 */
Test.prototype.toRemote = function() {
  return {
    name: this.name,
    title: this.title,
    status: this.status,
    failures: this.failures,
    checks: this.checks
  };
};

});
<|endoftext|>