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.
var
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
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 () {
var
// 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.
var
myTree = new BasicTree({hello: 'world'}),
nodeValue, lefties = [];
while (nodeValue = myTree.onward()) {
lefties.push(nodeValue);
}
//
// 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.
Panzer.version;
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.
Klass.id;
Retrieve, create, and list packages.
- Returns an array of package names.
js Klass.pkg();
- 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.
klass.ready;
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.
PkgDef.getSuper(name);
- 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.
PkgDef.index;
Unsubscribe from tank events.
PkgDef.off([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.
PkgDef.fn;
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.
pkg.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:
- Array.every
- Array.fill
- Array.filter
- Array.forEach
- Array.isArray
- Array.map
- Array.some
- Function.bind
- Promises
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 ...
</script>
Note: The minified file was compressed by Closure Compiler.
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.
require.config({
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