Skip to content

Latest commit

 

History

History
1100 lines (723 loc) · 36.5 KB

lib.md

File metadata and controls

1100 lines (723 loc) · 36.5 KB

Kung-Fig Lib References

Table of Contents

Basic Module Methods

Those are top-level module methods.

In all the following examples, it is assumed that var kungFig = require( 'kung-fig' ), and all the following are methods of the kungFig object.

.load( filePath , [options] )

  • filePath string the path of the file to load
  • options object (optional) an object of options, where:
    • cwd string override the Current Working Directory
    • reduce boolean (default: true) if true, the config is reduced (see .autoReduce())
    • doctype string or array of string (KFG only) if set, the KFG file to load MUST have a meta tag doctype and it should match the doctype string or be listed in the doctype array
    • kfgFiles object where:
      • extname array of string the extension list (without the initial dot) that should be treated as KFG files
      • basename array of string the filename list (including the extension part) that should be treated as KFG files
    • metaTagsHook function( metaContainer , options ) (KFG only) a callback triggered once the header part of the file is parsed, where:
      • metaContainer TagContainer the meta-tag container, see TagContainer
      • options object mostly reserved, where:
        • isInclude boolean true if the hook is not called for the top-level document's file but for an included document
        • file string the path of the file of the current document, if any
    • classes object (KFG only) each key is a class name and each value is the constructor to build the instance. Constructors are of type function( data ) where data is the parsed value used to instanciate the object.
    • tags object (KFG only) each key is a tag name and each value is the constructor to build the tag. Constructor are of type function( tagName , attributes , content , shouldParseAttributes , runtime ) where:
      • tagName string the name of the tag to instanciate
      • attributes mixed the attributes of the tag, if shouldParseAttributes is true, this is a string and it should be parsed
      • content mixed this is the parsed content of the tag
      • shouldParseAttributes boolean it true, the attributes argument is a raw string and should be parsed, if false the attributes argument contains the parsed argument, in whatever type the tag use (usually object)
      • runtime object reserved The prototype of the tag constructor MUST be an instance of Tag.prototype, see Tag.
    • metaTags object (KFG only) works just like the tags option but for meta-tags
    • operators object (KFG only) custom operators for Expression, the key is the operator string that should be in the Expression, the value is a function( args ) that should compute the operation, where args is an array of argument. See Expression.
    • modulePath object of string (optional) an object of module path that can be recognize substituted when a path like {module}/path/to/file is encountered. E.g.: if modulePath = { core: '/usr/lib/myapp/kfg-lib' }, and the file to load has to include {core}/toto.kfg, then it would include /usr/lib/myapp/kfg-lib/toto.kfg
    • baseDir string or array of string (optional) a list of allowed directories. If present, loading any file that is not descendant of one of those directories will throw an error. This is a security feature for apps running user content. All baseDir path MUST BE absolute path. No fs.realpath() are performed and it is not symlink bullet-proof: the app must not allow symlink creation by users.
    • fileObjectMap object reserved

This synchronously load the file using the filePath argument, parse it and return it.

If the document contains includes, there are all resolved synchronously before returning.

Available file types are:

  • KFG (*.kfg)
  • JSON (*.json)
  • Node.js Javascript module (*.js)
  • Raw text files (*.txt, or any unknown extension)

The path can be absolute, relative (to the Current Working Directory), or relative using the recursive parent search feature.

If the document contains tags that don't exist in the options.tags argument, there are instanciated using the built-in Tag constructor.

Simple document loading example:

var kungFig = require( 'kung-fig' ) ;
var document = kungFig.load( 'path/to/my/document.kfg' ) ;

.loadMetaTags( filePath , [options] )

  • filePath string the path of the file
  • options object (optional) an object of options, see .load()

This synchronously load the header/meta-tags of the file using the filePath argument, and parse it and return it, if any. It returns a TagContainer.

The body of the file is not parsed.

.saveJson( data , filePath , [options] )

  • data mixed the data to serialize
  • filePath string the path of the file
  • options object (optional) an object of options, where:
    • indent number or string (default: 2) how to indent the JSON, if its a number it's the number of spaces per indentation. This is the value passed as the third argument of JSON.stringify() Any other options are passed to fs.writeFileSync() as its third argument.

This serialize the data in the JSON format into the file, using the filePath argument.

This supports serialization of data structure containing circular references, because extra markup are hidden into property keys.

.saveKfg( data , filePath , [options] )

  • data mixed the data to serialize
  • filePath string the path of the file
  • options object (optional) an object of options passed to fs.writeFileSync() as its third argument.

This serialize the data in the KFG format into the file, using the filePath argument.

Some advanced features of the KFG format are not serializable at the moment, but will be available soon.

Circular references are supported.

The Dynamic Interface

The Dynamic interface/class is used to define dynamic objects suitable for scripting language. Bascially, a Dynamic object represents a thing that is not fully known at the time the object is created, and needs a context to be defined at runtime.

Different context produces different output values.

One example of a Dynamic object is a variable name, or a reference, inside a scripting language.

.get( ctx , [bound] ) or .getValue( ctx , [bound] )

  • ctx mixed the context, it can be of any type, but it is usually an object
  • bound boolean (optional) if true and the value to be returned is a function, the function is bounded to the relevant value, depending on the type of the Dynamic object (e.g. given a Ref instance "$obj.myfunc", the returned value would be bound to ctx.obj, like a regular Javascript method call would). This argument has no effect on some Dynamic objects where the bound concept is not relevant (e.g. TemplateSentence instances)

This get the value of a Dynamic instance (i.e. it solves it), using the provided ctx context. This should be idempotent as long as the values holded in the context do not change.

Dynamic.get( value , ctx , [bound] ) or Dynamic.getValue( value , ctx , [bound] )

This is the static method variant of .get().

The first argument value can be anything, if it is a Dynamic object, it will return value.get(), else it would return value.

.getFinalValue( ctx , [bound] )

  • ctx mixed the context, it can be of any type, but it is usually an object
  • bound boolean (optional) if true and the value to be returned is a function, the function is bounded to the relevant value, depending on the type of the Dynamic object (e.g. given a Ref instance "$obj.myfunc", the returned value would be bound to ctx.obj, like a regular Javascript method call would). This argument has no effect on some Dynamic objects where the bound concept is not relevant (e.g. TemplateSentence instances)

Like .get(), it solves a Dynamic instance using the ctx context However, as long as the result itself is a Dynamic instance, it is solved again.

E.g. a Ref instance can reference another ref, that itself reference another ref, and so on... This will solve the whole reference chain until a non-Dynamic value is found.

Dynamic.getFinalValue( value , ctx , [bound] )

This is the static method variant of .getFinalValue().

The first argument value can be anything, if it is a Dynamic object, it will return value.getFinalValue(), else it would return value.

.getDeepFinalValue( ctx , [bound] )

  • ctx mixed the context, it can be of any type, but it is usually an object
  • bound boolean (optional) if true and the value to be returned is a function, the function is bounded to the relevant value, depending on the type of the Dynamic object (e.g. given a Ref instance "$obj.myfunc", the returned value would be bound to ctx.obj, like a regular Javascript method call would). This argument has no effect on some Dynamic objects where the bound concept is not relevant (e.g. TemplateSentence instances)

Like .getFinalValue(), a Dynamic chain is solved until a non-Dynamic value is found.

Then, if the final value is an object or an array, it will search recursively for Dynamic object inside it, and if any, it would apply .getFinalValue() on them.

Dynamic.getDeepFinalValue( value , ctx , [bound] )

This is the static method variant of .getDeepFinalValue().

The first argument value can be anything, if it is a Dynamic object, it would return value.getDeepFinalValue(), else it would return value.

.extractFromStatic( ctx , [bound] )

  • ctx mixed the context, it can be of any type, but it is usually an object
  • bound boolean (optional) if true and the value to be returned is a function, the function is bounded to the relevant value, depending on the type of the Dynamic object (e.g. given a Ref instance "$obj.myfunc", the returned value would be bound to ctx.obj, like a regular Javascript method call would). This argument has no effect on some Dynamic objects where the bound concept is not relevant (e.g. TemplateSentence instances)

Like .getFinalValue(), a Dynamic chain is solved until a non-Dynamic value is found.

Then, if the final value is an object or an array, it will search recursively for Dynamic object inside it, and if any, it would apply .getFinalValue() on them.

Dynamic.extractFromStatic( value , ctx , [bound] )

This is the static method variant of .extractFromStatic().

The first argument value can be anything, if it is a Dynamic object, it would return value.extractFromStatic(), else it would return value.

.toString( ctx )

  • ctx mixed the context, it can be of any type, but it is usually an object

This get the final value of the Dynamic and cast it to a string, if possible.

.apply( ctx , [bound] )

  • ctx mixed the context, it can be of any type, but it is usually an object
  • bound boolean (optional) if true and the value to be returned is a function, the function is bounded to the relevant value, depending on the type of the Dynamic object (e.g. given a Ref instance "$obj.myfunc", the returned value would be bound to ctx.obj, like a regular Javascript method call would). This argument has no effect on some Dynamic objects where the bound concept is not relevant (e.g. TemplateSentence instances)

Some Dynamic instance don't have the dynamic flag on but instead the applicable flag.

There is not much difference between dynamic and applicable, but all .get*() methods will do nothing on an applicable, (i.e. those method would return the Dynamic instance untouched), instead, .apply() should be used.

.apply() produce exactly the same output than .get() does, except that it works on applicable only, where .get() works on dynamic only.

Think of applicable as a sort of locked Dynamic object.

Applicable are an important concept in KFG.

Here is a fragment of Spellcast scripting (a scripting language built on top of KFG) to explain why applicable Dynamic object are useful:

# Set the var named $you to "Bob"
[set $you] Bob

# Set the var named $message to "Hello Bob!",
# solving the template "Hello ${you}!" immediately
[set $message] $> Hello ${you}!

# Set the var named $template to the template "Hello ${you}!",
# $template will be an applicable object and will never been solved
# unless the [apply] tag is called on it explicitly
[set $template] $$> Hello ${you}!

# Output "Hello Bob!"
[message] $message

# Apply the template now and put the value inside the $applied var
[apply $template => $applied] $

# Output "Hello Bob!"
[message] $applied

# Now we set $you to a different value: "Jack"
[set $you] Jack

# It still output "Hello Bob!", $message is indeed a string
[message] $message

# This apply the template but now the variable $you used
# to solve the template is not "Bob" anymore but "Jack"
[apply $template => $applied] $

# So this output "Hello Jack!"
[message] $applied

Dynamic.apply( value , ctx , [bound] )

This is the static method variant of .apply().

The first argument value can be anything, if it is a Dynamic object, it would return value.apply(), else it would return value.

.set( ctx , value )

  • ctx mixed the context, it can be of any type, but it is usually an object
  • value mixed the value to set on the Dynamic instance

This set the value of a Dynamic instance, using the provided ctx context. Not all Dynamic instance supports .set(), it does not always make sense.

The Ref Class

Refs are useful for building scripting language on top of KFG: they represent variables, or paths to variable. To solve a ref, a context is needed. The ref is simply a path to walk in that context, it point to a data in the context.

It implements the Dynamic interface.

Let's see a ref in action:

var kungFig = require( 'kung-fig' ) ;

// First define a context
var ctx = {
	a: 1 ,
	nested: {
		b: 2
	}
} ;

// Parse and create a ref
var myref = kungFig.Ref.parse( '$nested.b' ) ;

// Output '2', the value of ctx.nested.b
console.log( myref.get( ctx ) ) ;

Refs always start with a $. See the KFG Ref syntax.

Refs may contain refs in their paths, e.g. kungFig.Ref.parse( "$path.to[$key1][$key2]" ). Or even any depth-level of nested refs: kungFig.Ref.parse( "$path.to[$path.to.keys[$key]]" ).

new Ref( arg )

  • arg mixed

Create a Ref instance, call .setRef() with arg and return the instance.

Ref.parse( str )

  • str string a KFG Ref string to parse

This parses a ref and returns a Ref instance. See the KFG Ref syntax.

.setRef( arg )

  • arg mixed, the type is either:
    • string this set the ref to this KFG Ref string, see the KFG Ref syntax
    • array this set the ref to this parsed ref (internal use)
    • null or undefined nullify the ref

Set the internal reference value.

.get( ctx , [bound] , [getArray] ) or .getValue( ctx , [bound] , [getArray] )

  • ctx object or array the context
  • bound boolean (optional) if true and the value to be returned is a function, the function is bounded e.g. given a Ref instance "$obj.myfunc", assuming ctx.obj.myfunc is a function, the returned value would be the function ctx.obj.myfunc bounded to ctx.obj, like a regular Javascript method call would.
  • getArray boolean if true, instead of returning the solved value, it returns an array containing the solved value, the solved value without the last part of the path, and the last part of the path, e.g. "$path.to.value" would return this array: [ ctx.path.to.value , ctx.path.to , "value" ]

This get a value out of the ctx context, by using the path of the ref. If the path cannot be solved, undefined is returned (or [] if the getArray option was set).

Example:

var kungFig = require( 'kung-fig' ) ;

// First define a context
var ctx = {
	a: 1 ,
	nested: {
		b: 2
	}
} ;

// Parse and create a ref
var myref = kungFig.Ref.parse( '$nested.b' ) ;

// Output '2', the value of ctx.nested.b
console.log( myref.get( ctx ) ) ;

.set( ctx , value )

  • ctx object or array the context
  • value mixed the value to set where the ref point to in the context

Following the path of the ref, this set a value inside the ctx context.

If the path does not exist inside the context, it creates as many objects and/or arrays needed along the way.

Example:

var kungFig = require( 'kung-fig' ) ;

// First define a context
var ctx = {
	nested: {
		b: "old value"
	}
} ;

// Parse and create a ref
var myref = kungFig.Ref.parse( '$nested.b' ) ;

// Output "old value", the value of ctx.nested.b
console.log( myref.get( ctx ) ) ;

// Set ctx.nested.b to "new value"
myref.set( ctx , "new value" ) ;

// Output "new value", the new value of ctx.nested.b
console.log( myref.get( ctx ) ) ;

// Create a new ref with a path that does not exist in ctx ATM
myref = kungFig.Ref.parse( '$an.unexistant.path' ) ;

// Set ctx.an.unexistant.path to "bouh!", creating all objects along the way
myref.set( ctx , "bouh!" ) ;

/*
So ctx is now equal to:
{
	nested: {
		b: "new value"
	} ,
	an: {
		unexistant: {
			path: "bouh!"
		}
	}
} ;
*/

The TemplateSentence Class

Template sentences are useful for building scripting language on top of KFG: they are internationalizable templates, containing references, and a lot of tools to ease human language.

The TemplateSentence class uses Babel Tower behind the scene, and encapsulate a Babel.Sentence instance behind a Dynamic interface.

Like all Dynamic object, to solve a template sentence, a context is needed.

Let's see a template sentence in action:

var kungFig = require( 'kung-fig' ) ;

// First define a context
var ctx = {
	name: "Bob"
} ;

// Create the template
var template = new kungFig.TemplateSentence( "Hello ${name}!" ) ;

// Output "Hello Bob!", using the string in ctx.name
console.log( template.get( ctx ) ) ;

This is a really simple example, and template sentences can do a lot more. See the KFG TemplateSentence syntax and the Babel Tower documentation for more.

The Babel object used for internationalization/localization is the __babel property of the context, if it's not defined, or it will fallback to the global Babel.default instance.

Example featuring i18n/l10n:

var kungFig = require( 'kung-fig' ) ;
var babel = new require( 'babel-tower' )() ;

babel.extend( {
	fr: {
		sentences: {
			"Give me ${count} apple${count}[n?|s]!" : "Donne-moi ${count} pomme${count}[n?|s]!"
		}
	}
} ) ;

// Define the context
var ctx = {
	__babel: babel ,
	count: 2
} ;

// Create the template
var template = new kungFig.TemplateSentence( "Give me ${count} apple${count}[n?|s]!" ) ;

// Output "Give me 2 apples!"
console.log( template.get( ctx ) ) ;

// Switch to the 'fr' (french) locale
ctx.__babel.setLocale( 'fr' ) ;

// Output "Donne-moi 2 pommes!"
console.log( template.get( ctx ) ) ;

new TemplateSentence( template )

  • template string the string used as template

This creates a TemplateSentence instance from a string and return it.

The TemplateAtom Class

A template atom is a used as a part of a template sentence.

The TemplateAtom class uses Babel Tower behind the scene, and encapsulate a Babel.Atom instance behind a Dynamic interface.

Like all Dynamic object, to solve a templateAtom, a context is needed.

Think of template atom as a part of a sentence, it can be a noun, a noun group.

A template atom can have alternative strings according to its gender and number, it can have a translatable key, it can be just a number...

Example featuring i18n/l10n and template atoms:

var kungFig = require( 'kung-fig' ) ;
var babel = new require( 'babel-tower' )() ;

babel.extend( {
	fr: {
		sentences: {
			"Hello ${who}[g:m]!" : "Bonjour ${who}[g:m]!" ,
			"Hello ${who}[g:f]!" : "Bonjour ${who}[g:f]!"
		} ,
		atoms: {
			master: { altg: [ 'maitre' , 'maitresse' ] }
		}
	}
} ) ;

// Define the context
var ctx = {
	__babel: babel ,
	
	/*
		Create an atom having:
		* a translatable key: master
		* a male gender alternative: master
		* a female gender alternative: mistress
	*/
	who: new kungFig.TemplateAtom( "master[g?master|mistress]" )
} ;

// Create two templates using the atom, the first one use the male version ([g:m])
// the last one use the female version ([g:f])
var template1 = new kungFig.TemplateSentence( "Hello ${who}[g:m]!" ) ;
var template2 = new kungFig.TemplateSentence( "Hello ${who}[g:f]!" ) ;

// Output "Hello master!"
console.log( template1.get( ctx ) ) ;

// Output "Hello mistress!"
console.log( template2.get( ctx ) ) ;

// Switch to the 'fr' (french) locale
ctx.__babel.setLocale( 'fr' ) ;

// Output "Bonjour maitre!"
console.log( template1.get( ctx ) ) ;

// Output "Bonjour maitress!"
console.log( template2.get( ctx ) ) ;

new TemplateAtom( arg )

  • arg mixed

This creates a TemplateAtom instance, the arg argument is passed as the first argument of Babel.Atom(). See Babel Tower documentation.

The Expression Class

Expressions are useful for building scripting language on top of KFG: they provide arithmetical expression, logic expression, various math functions, and more...

It implements behind the Dynamic interface.

Like all Dynamic object, to solve an expression, a context is needed.

Let's see an expression in action:

var kungFig = require( 'kung-fig' ) ;

// First define a context
var ctx = {
	a: 7 ,
	b: 5
} ;

// Parse and create 2 expressions
var exp1 = kungFig.Expression.parse( '1 + 1' ) ;
var exp2 = kungFig.Expression.parse( '$a + $b' ) ;

// Output '2', the value of 1 + 1
console.log( exp1.get( ctx ) ) ;

// Output '12', the value of ctx.a + ctx.b
console.log( exp2.get( ctx ) ) ;

An expression is basically an operator (i.e. a function) and an array of operands. An operand can be anything, even a Dynamic object, such like refs, or another expression...

First, all Dynamic objects operands are solved using the provided context. Once done, the operator function is called with the solved array of operands.

See the KFG expression documentation for the expression syntax and the built-in operators.

Expression.parse( str , [customOperators] )

  • str string the expression string
  • customOperators object (optional) where keys are operator identifiers and value a function( operands ), where:
    • operands array the array of operands

This parses and returns an Expression. Custom operators cannot override built-in operators.

Example using a custom operator:

var kungFig = require( 'kung-fig' ) ;

// First define a context
var ctx = {
	a: 7 ,
	b: 5
} ;

// Parse and create 2 expressions
var exp = kungFig.Expression.parse( 'my-operator $a $b' , {
	"my-operator": function( operands ) {
		return ( operands[ 0 ] + 1 ) / ( operands[ 1 ] + 1 ) ;
	}
} ) ;

// Output 1.333333, the value of ( ctx.a + 1 ) / ( ctx.b + 1 )
console.log( exp.get( ctx ) ) ;

new Expression( fnOperator , operands )

  • fnOperator function( operands ) the operator's function, where:
    • operands array the array of operands
  • operands array the array of operands

This creates an Expression instance and returns it.

The Tag Class

Tag are useful for building scripting language on top of KFG: they often represent actions.

The Tag class is very generic, it is used as the super class for more specific tags that the userland scripting language should define. Most of time, you wont derive from Tag itself but from one of the built-in derived class.

A tag instance has those properties:

  • name string the name of the tag
  • parent TagContainer this is the parent TagContainer instance or null if it has no parent
  • attributes mixed the tag attributes, it can be a string or object or anything, it all depends on the derived class
  • content mixed the content of the tag, it can be anything

new Tag( tagName , attributes , [content] , [shouldParseAttributes] , [options] )

  • tagName string the name of the tag, assigned to the name property
  • attributes mixed the tag attributes, assigned to the attributes property
  • content mixed (optional) the content of the tag, assigned to the content property
  • shouldParseAttributes boolean (optional) true if the attributes argument is a raw string that needs to be parsed
  • options: (optional) RESERVED

If shouldParseAttributes is on, it calls the tag.parseAttributes() with the attributes argument, the result is assigned to the attributes property.

.parseAttributes( str )

  • str string the raw attribute string to be parsed

This method returns a parsed attribute (a string, an object or whatever) from a raw attribute string.

Most of time, this method should be overloaded to get a meaningful output. By default it just returns the trimmed raw string.

.stringifyAttributes()

This method returns the stringified (i.e. serialized) attributes.

Most of time, this method should be overloaded to get a meaningful output. By default it just returns the trimmed attributes property if it's a string, otherwise an empty string.

.getParentTag()

This method returns the parent tag, if any. Otherwise it returns null.

.extractContent( ctx , [bound] )

  • ctx mixed the context, it can be of any type, but it is usually an object
  • bound boolean (optional) if true and the value to be returned is a function, the function is bounded to the relevant value, depending on the type of the Dynamic object (e.g. given a Ref instance "$obj.myfunc", the returned value would be bound to ctx.obj, like a regular Javascript method call would). This argument has no effect on some Dynamic objects where the bound concept is not relevant (e.g. TemplateSentence instances)

This method extract the tag's content (i.e. the content preoperty). The content of a tag is always deep-cloned from the static content attached to the tags, except for object considered immutable (note that all unknown instances are considered immutable by default). This is because tags are mostly used to create scripting language, and a tag can be executed multiple times and should create each time a new independent value. Technically, it calls Dynamic.extractFromStatic( this.content , ctx , bound ).

Built-in Tag Derived Class

There are various built-in Tag available out of the box. They have their own attributes parser.

LabelTag Class

This Tag is simply aware of quoted-string.

So both KFG tags [mytag label] and [mytag "label"] would produce attributes=label, where a simple Tag instance would produce attributes="label" for the later KFG syntax.

VarTag Class

This Tag is used when an attributes should be a ref.

So this KFG tags [mytag $path.to.data] would produce attributes=Ref.parse( "$path.to.data" ).

ClassicTag Class

This Tag uses a syntax similar to XML/HTML.

Supported attribute values:

  • numbers
  • quoted strings
  • refs

Given the KFG tag [mytag key1=123 key2="value" key3=$path.to.data], it would produce the attributes: { "key1": 123 , "key2": "value" , "key3": Ref.parse( "$path.to.data" ) }.

ExpressionTag Class

This Tag is used when the attribute should be an expression

Given the KFG tag [my-if-tag $a > $b], it would produce the attributes:Expression.parse( "$a > $b" ).

The TagContainer Class

TagContainers are implicitly created by the KFG parser as soon as a node has a tag as its child. It has some methods

A tag container instance has those properties:

  • tag Tag the parent Tag instance, if any, otherwise null.
  • children array an array of Tag instances

new TagContainer( [children] , [tag] )

  • children array (optional) an array of Tag instances, assigned to the children property, default to an empty array.
  • tag Tag (optional) a parent Tag instance, assigned to the tag property, default to null.

.get( id )

  • id mixed the tag ID

It searches the tag container for a tag matching this ID and returns it, otherwise it returns undefined. The Tag instance should has an id property, this is something the Tag don't have, but that could exist in a derived class. It stops at the first matching ID, that's the duty of the userland code to ensure that the ID is unique.

.getTags( tagName )

  • tagName string the tag name to search for

It searches the tag container for all tags matching the provided tagName, and returns them in an array.

.getFirstTag( tagName )

  • tagName string the tag name to search for

It searches the tag container for the first tag matching the provided tagName, and returns it. It returns undefined if the tag is not found.

.getUniqueTag( tagName )

  • tagName string the tag name to search for

It searches the tag container for the unique tag matching the provided tagName, and returns it. If the tag is not found or if there are more than one matching tags, it throws an error.

Tree Operations

Kung Fig has a special tree operations syntax. Tree operations merge multiple trees (i.e. deep nested object structure) according to some rules. It works mostly like a regular deep object extensions most Javascript coders are used to, except that some specials operators can be placed at the begining of some properties' key.

Let's see tree operations in action:

var creature = {
	hp: 8 ,
	attack: 5 ,
	defense: 4
} ;

var amulet = {
	"+defense": 1 ,
	"+hp": 2
} ;

var stats = kungFig.reduce( creature , amulet ) ;

The stats var equals to: { hp: 10 , attack: 5 , defense: 5 }. The two objects are merged, but the second object contains operators: the +defense means add the value of this +defense property to the value of the defense property, then delete +defense. The same applies with the +hp property.

Also it would have created the same stats var if all properties were in the same object:

var creature = {
	hp: 8 ,
	attack: 5 ,
	defense: 4 ,
	"+defense": 1 ,
	"+hp": 2
} ;

var stats = kungFig.reduce( creature ) ;

Most of time, you would not create object having tree-operations operators inside JS code source, but you would just use them in a KFG file, then process them in JS with the methods below. See the whole operators list here.

.reduce( object1 , [object2] , [...] )

  • object1 object the object to reduce
  • object2, ... object other objects to merge and reduce with object1

It reduces one or many objects, i.e. it extends and applies operator properties, and returns the result. It does not modify any objects, but creates a brand new one.

.autoReduce( object1 , [object2] , [...] )

  • object1 object the object to reduce in-place
  • object2, ... object other objects to merge and reduce with object1

It works like .reduce() except that it does not creates a new object, instead all operations are applied on the first argument (object1) in-place. It returns the modified object1.

.toObject( object )

  • src object the source object

It returns an object where all property-operators have been deeply (i.e. recursively) removed, and where escaped keys are unescaped.

Example:

var creature = {
	hp: 8 ,
	attack: 5 ,
	defense: 4 ,
	"+defense": 1 ,
	"+hp": 2
} ;

console.log( kungFig.toObject( creature ) ) ;

This will output { hp: 8 , attack: 5 , defense: 4 }.

.reduceToObject( object1 , [object2] , [...] )

  • object1 object the object to reduce
  • object2, ... object other objects to merge and reduce with object1

It reduces one or many objects, i.e. it extends and applies operator properties, and returns the result where all remaining property-operators have been deeply (recursively) removed, and all escaped keys are unescaped.

This is a shorthand for: kungFig.toObject( kungFig.reduce( object1 , [object2] , [...] ) ).