Fetching contributors…
Cannot retrieve contributors at this time
468 lines (294 sloc) 13 KB


Build Status

version 0.4.0 by Bemi Faison

A comprehensive node-tree solution, for smart data.*


Panzer is a JavaScript utility for defining classes that normalize data into node-trees, then implement hierarchical and/or navigation-based logic. Use Panzer to author classes that codify the intent of, and expose relationships in, structured data.

Below is a trivial example, using Panzer to render nested ULs.

  ULMaker = Panzer.create(),
  makerPkg = ULMaker.pkg('maker');

makerPkg.badKey = function (name, value) {
  return typeof value !== 'string' || typeof value !== 'object';

* Read The Cathedral and the Bazaar, to understand why "Smart data structures and dumb code works a lot better than the other way around." -- Eric S. Raymond


Because the problem domain is vast (i.e., everything can be described with a data structure), Panzer offers a bevy of features for you to use a la carte. The following are either covered in the Usage section below, pending documentation, or amongst Panzer's 400+ unit tests:

  • Current state & position
  • Delegate classes and configurations
  • Event object reflection
  • Navigation control interface
  • Navigation loop & events life-cycle
  • Nodal behavior & logic
  • Node & attribute parsing
  • Promise-enabled parsing, initialization, and navigation
  • Prototypal inheritance, inspection and reflection
  • Reusable instances

Customizing with Packages

Parsing Values

Identifying Attributes
Ignoring Object Members
Processing Nodes
Asynchronous Node Processing

Navigating the Node-Tree

First, create a Panzer class.

var BasicTree = Panzer.create();

In order to access and extend Panzer's functionality, we need to define a package. (Otherwise, our instances won't do much.) Let's register a new package by name.

var myPkgDef = BasicTree.pkg('my first package');

The returned value is the package-definition, a function with special members that hook into our class. Many hooks are available, but we'll prototype an instance method now.

myPkgDef.fn.onward = function () {
        // get the corresponding package-instance
        tree = myPkgDef(this),
        // alias the tank that controls our tree's "pointer"
        tank = tree.tank,
        // reference the current node
        currentNode = tree.nodes[tank.currentIndex],
        // reference a sibling or child node, if available
        nextNode = tree.nodes[currentNode.nextIndex || currentNode.firstChildIndex];
    // if there is a nextNode and we've successfully navigated to it...
    if (nextNode && tank.go(nextNode.index)) {
        // return the value of (what is now) the current node
        return nextNode.value;
    // (otherwise) flag that no node is next
    return false;

Though simple enough, there is a lot going on in our method. Nevertheless, we can now "walk" the left-branch of any data structure.

    myTree = new BasicTree({hello: 'world'}),
    nodeValue, lefties = [];
while (nodeValue = myTree.onward()) {
// lefties[0] -> {hello: 'world'}
// lefties[1] -> 'world'

Understanding the package api is key to getting the most from your class. However, in lieu of full documentation, please see the test suite for greater insight. Thanks for your patience!


This section serves as reference documentation to using the Panzer module. The module exports a Panzer namespace.

Instance methods are prefixed with a pound-symbol (#). Instance properties are prefixed with an at-symbol (@). Static members (both properties and methods) are prefixed with a double-colon (::).


Create a unique Panzer class.

var Klass = Panzer.create();

Returns a constructor function, informally called a Klass.


The SemVer compatible version string.



Instantiate a Klass instance.

new Klass([source [, config]]);
  • source: (mix) A value to be parsed into nodes.
  • config: (object) Used by packages, during initialization.

Returns a promise when invoked without new. The promise resolves with the initialized Klass instance.

The Klass prototype inherits from it's package (PkgDef.fn).


A unique numeric identifier.;


Retrieve, create, and list packages.

  1. Returns an array of package names. js Klass.pkg();
  2. Returns a new or existing package. js Klass.pkg(name);
    • name: (string) An alphanumeric string.

Throws when given anything but an alphanumeric string.

A package is a delegate class, event subscriber, and collection of tree parsing rules and logic. A Klass may many packages.


A promise that resolves after this instance has initialized.


This instance property is a thenable, from which you may use Promise.then.

The promise resolves with the initialized Klass instance.


A rule that determines whether an object member should be parsed as a node attribute.

PkgDef.attrKey = rule;

By default, rule is false and may be any falsy value.

When a string, during parsing, member keys that begin with this rule become node attributes. (The comparison is case-sensitive.)

When a regular expression, during parsing, member keys that match this rule become node attributes.

When a function, during parsing, returning a truthy value makes this member a node attribute. Functions receive two arguments, the member name and value. (The first member name is an empty string.)


A rule that determines whether an object member should not be excluded from the node-tree.

PkgDef.badKey = rule;

By default, rule is false and may be any falsy value.

When a string, during parsing, member keys that begin with this rule are excluded from the node-tree. (The comparison is case-sensitive.)

When a regular expression, during parsing, member keys that match this rule are excluded from the node-tree.

When a function, during parsing, returning a truthy value excludes this member from the node-tree. Functions receive two arguments, the member name and value. (The first member name is an empty string.)


Indicates when a Klass instance's nodes may be copied for a new instance.

PkgDef.cloneable = setting;

By default, setting is true and may be any truthy value.

With more than one package, all must use a truthy value. If not, the new proxy object will parse the given proxy object's original source value.


Retrieve the same-name member from an older Package's Klass prototype.

  • name (string) name of the member to retrieve.

Returns undefined when the member is not defined.


The zero-index order that this package was defined.



Unsubscribe from tank events.[name [, callback]]);
  • name - (string) The name of the event to stop listening.
  • callback - (function) The method that was previously subscribed.

Returns the same package definition object.

Removes all subscribers (via this package), when called without arguments.

Removes all subscribers to the given event (via this package), if callback is omitted.

Removes the given callback from the given event (subscribed via this package), when called with both arguments.


Subscribe to tank events.

PkgDef.on([name [, callback]]);

Returns the same package definition object.

  • name - (string|strings) The event name (or array of event names) to observe.
  • callback - (function) The method to invoke when the event is triggered.


Determines how and which object members are parsed into nodes.

PkgDef.prepNode = funcRef;

funcRef is a function that is called during instantiation, and returns what will be the node's value. Within the function, this is the global/window object. (Members of the returned value are further parsed.) If a Promise is returned, the resolved value is used.

The first argument passed to the specified function is the object's member's name, as a string. This argument is an empty string, when first called, per instantiation.

The second argument passed to the specified function is the object's member's value. This argument is the value passed to the Klass function, when first called, per instantiation.


An inherited chain of the Klass prototype.


Like any prototype Members of fn are available to all Klass instances. Precedence is given to the most recently registered Package.


An array of nodes.


Each node is an object that represents a position in the node-tree, along with a value - parsed from the value parsed from the Klass instance.

This array is empty (until initialization), while node-parsing and/or initialization is delayed.

describing their structure and position, and value, respectively.

Each node is an Object instance with the following properties:

* **attrs** :
* **childIndex** :
* **children** :
* **depth** :
* **firstChildIndex** :
* **index** :
* **lastChildIndex** :
* **nextIndex** :
* **parentIndex** :
* **path** :
* **previousIndex** :
* **value** :






























Panzer works within, and is intended for, modern JavaScript environments. It is available on bower, component and npm as a UMD module (supporting both CommonJS and AMD loaders).

If Panzer isn't compatible with your favorite runtime, please file an issue or - better still - make a pull-request.


Panzer has no module dependencies. Panzer should work wherever these ECMAScript 5 & 6 features are native or polyfilled:

Web Browsers

Use a <SCRIPT> tag to load the panzer.min.js file in your web page. Doing so, adds Panzer to the global scope.

  <script type="text/javascript" src="path/to/panzer.min.js"></script>
  <script type="text/javascript">
    // ... Panzer dependent code ...

Note: The minified file was compressed by Closure Compiler.

Package Managers

  • npm install panzer
  • component install bemson/panzer
  • bower install panzer


Assuming you have a require.js compatible loader, configure an alias for the Panzer module (the term "panzer" is recommended, for consistency). The panzer module exports a module namespace.

  paths: {
    panzer: 'my/libs/panzer'

Then require and use the module in your application code:

require(['panzer'], function (Panzer) {
  // ... Panzer dependent code ...

Note: Prefer AMD optimizers, like r.js, over loading the minified file.


Panzer is available under the terms of the MIT-License.

Copyright 2017, Bemi Faison