Anatomy of a Control

For the software developer under a deadline, sometimes a thoughtful comment in source code can make all the difference. With that in mind, here's a look at the kind definition for a control called PrevNextBanner:

// enyo.kind is our constructor factory.
//
//    - a constructor is a function-intended-as-an-argument-to-new
//        (we often abbreviate as 'ctor').
//        e.g. foo = new ctor(); // "ctor" is the constructor
//    - enyo.kind generates constructors.
//
// The factory builds a constructor with a number of value-adds which
// come from built in features, global pluggable features, and prototype-
// specific pluggable features.
//
// The argument to the factory is a single object we call a
// 'configuration'. Configurations are also used in 'components' and
// 'chrome' blocks, and in calls to opus.create.
//
// In this way, a data object can interchangeably describe an instance or
// a constructor (exception: the 'create' method, which is only supported
// for constructor configurations).
//
enyo.kind({
  // - automatically prepares namespace and stores a reference to the
  //  constructor
  //  i.e. enyo.PrevNextBanner will reference the built constructor.
  name: "enyo.PrevNextBanner",

  // - prototype-chaining (aka inheritance): the prototype of the
  //  prototype for this constructor is enyo.HFlexBox.
  //  We can say this constructor is a 'kind' of enyo.HFlexBox.
  kind: enyo.HFlexBox,

  // - this is a basic property assignment. 'align' property is supported
  //  by HFlexBox
  align: "center",

  // - enyo.Object constructor plug-in support for 'published' properties.
  //  Published properties have automatic getter/setter/changed support.
  //  I.e. publishing a property called 'foo' means the constructor will
  //  have getFoo(), setFoo(<value>), and fooChanged(<newValue>, <oldValue>)
  //  methods.  Note that Object also exposes generic getProperty(<name>)
  //  and setProperty(<name>, <value>) methods.
  published: {
      previousDisabled: false,
      nextDisabled: false
  },

  // - enyo.Component constructor plug-in support for 'event' properties.
  //  Event properties allow delegation of methods to client objects.
  //  I.e. publishing an event called 'onFoo' establishes a doFoo(...)
  //  method that essentially calls this.owner[this.onFoo](this, ...).
  events: {
      onPrevious: "bannerPrevious",
      onNext: "bannerNext"
  },

  // - components is an array of configurations that describe Components
  //  owned by this object.
  components: [
      {name: "previous", kind: enyo.Button, className: "enyo-banner-prev",
          onclick: "doPrevious"},
      {name: "client", kind: enyo.HFlexBox, flex: 1, align: "center",
          className: "enyo-banner-content"},
      {name: "next", kind: enyo.Button, className: "enyo-banner-next",
          onclick: "doNext"}
  ],

  create: function()
      // - part of the DomNode interface, add this CSS class to our
      //  rendered DOM
      this.addClass('enyo-prev-next-banner');

      // - this.inherited(arguments[, newArgsArray]) calls the inherited
      //  version of this method (the nearest implementation of this
      //  method in the prototype chain).
      //  'arguments' required parameter is literally the JavaScript
      //  special value 'arguments'.
      //  'newArgsArray' can be used to send a new argument list for the
      //  inherited method.
      //  Note that you can call any inherited method manually with
      //  this syntax:
      //      <name of constructor>.prototype.<method name>.apply(<this>,
      //          <arguments array>)
      //      e.g. enyo.HFlexBox.prototype.create.apply(this,
      //          [{align: "center"}]);
      this.inherited(arguments);

      // - Constructors that inherit from Component can take a
      //  configuration block as an argument. Component copies those
      //  configuration values into instance properties, but does not
      //  call *changed methods automatically. It's up to each
      //  constructor to call *changed for any properties that might
      //  need side-effects.
      this.contentChanged();
      this.nextDisabledChanged();
      this.previousDisabledChanged();
  },

  // - 'content' property is inherited from Control. Here this property
  //  is simply propagated to this.$.client.
  //  the inherited method is not called, so normal contentChanged
  //  processing is aborted.
  contentChanged: function() {
      this.$.client.setContent(this.content);
  },

  // - next/previousDisabled properties are propagated directly to the
  //  underlying buttons.
  nextDisabledChanged: function() {
      this.$.next.setDisabled(this.nextDisabled);
  },
  previousDisabledChanged: function() {
      this.$.previous.setDisabled(this.previousDisabled);
  }
});