* * *
I want <span class="TKAdjustableNumber" data-var="cookies"></span> cookies.
The class
attribute normally specifies a CSS class which gives an element a style. With Tangle, it can also specify a JavaScript class which gives an element a behavior. It might turn an element into a control that adjusts a variable, or a view that displays a variable. It is common to define a class in both CSS and Tangle, for appearance and behavior respectively.
A number of basic classes are included with TangleKit. For details on how to create and plug in your own classes, see Tangle.classes.
An element can have any number of classes, like so:
<span class="MyClass MyOtherClass" data-var="cookies">
Any of the classes can be defined in CSS, JavaScript, both, or neither. You might use one class for the visual presentation of a variable, and another to provide interactive manipulation.
I ate <span data-var="cookies"></span> cookies.
The data-var
attribute specifies a variable. You typically use it to display the variable's value in some way, or to specify which variable a class should adjust.
The data-var
attribute can go on any element. If the element has a class, the variable name will be passed to the class's initialize
method, and the variable's value will be passed to the class's update
method whenever it changes.
If none of the element's classes has an update
method, Tangle will insert the variable's value into the element (before any existing content), and update it whenever it changes. The value will be formatted with the data-format
attribute, if present. Tangle won't insert anything if any class has an update
method, because it assumes that class is handling the display of the variable.
An element can have multiple variables, like so:
<span class="MyClass" data-var="cookies tacos">
All variable names will be passed to MyClass's initialize
method, and all values will be passed to MyClass's update
method. This is useful for two-dimensional controls that adjust two variables at once, or views that depend on multiple variables. See Tangle.classes for details.
That cookie costs $<span data-var="cost" data-format="%.2f"></span>.
The variable is presented with the given format. You can specify a printf-style format, or define your own format functions. To use printf-style formats, you must include a sprintf library, such as the one that is provided with TangleKit. For details on defining custom formats, see Tangle.formats.
If you specify multiple variables in the data-var
attribute, all values will be passed to the format:
The cookies cost $<span data-var="cost quantity" data-format="%.2f per %d"></span>.
If you are using a custom format, you can pass parameters to it, separated by spaces. See Tangle.formats for details.
<span data-var="cookies" data-format="myFormat parameter1 parameter2">
Classes may make use of other attributes with the data-
prefix. For example, specifying the minimum and maximum values for a TKAdjustableNumber
:
<span class="TKAdjustableNumber" data-var="cookies" data-min="0" data-max="20">
These additional attributes are passed to the class's initialize
method, via the options
parameter.
For details on what attributes a class accepts, see the documentation for the individual class.
* * *
var tangle = new Tangle (rootElement, model);
var tangle = new Tangle (document.getElementById("calorieCalculator"), { initialize: function () { this.cookies = 4; }, update: function () { this.calories = this.cookies * 50; } });
Creates a new tangle. Tangle looks under rootElement for elements with classes or variables. Classes are initialized, and the model is set as described below.
You may create as many tangles as you like, with different rootElements or models. Each one keeps track of its own variables and classes. Typically you will create a tangle for each "interactive widget" in your document.
tangle.setModel(newModel);
tangle.setModel({ initialize: function () { this.cookies = 4; }, update: function () { this.calories = this.cookies * 50; } });
This method changes and resets the current model.
The model should be an object with initialize
and update
methods. When the model is set, the initialize
method is called to set the initial values of variables, and the update
method is called to calculate variables derived from those. When a variable is changed subsequently via tangle.setValue
, the update
method will be called again to recalculate derived variables.
You may also define any methods that you want to use internally:
{ initialize: function () { this.cookies = 4; }, update: function () { this.calories = this.cookies * this.getCalPerCookie(); }, getCalPerCookie: function () { ... } }
var value = tangle.getValue(variable);
var numberOfCookies = tangle.getValue("cookies");
Returns the current value of a variable.
tangle.setValue(variable, newValue);
tangle.setValue("cookies", 17);
Sets a new value for a variable. If the new value is different than the old one, the model is updated.
A variable is typically a number, but can be of any type. If you are using a reference type such as an Array, be aware that you cannot update the model by mutating the array. Instead, pass a new or copied array to tangle.setValue
.
tangle.setValues({ variableName:value, variableName:value });
tangle.setValues({ cookies:17, cupcakes:12 });
Sets values for multiple variables at once.
* * *
Tangle.classes.MyClass = { initialize: function (element, options, tangle, variable) { ... }, // optional update: function (element, value) { ... } // optional };
A Tangle class lets you associate some JavaScript code with an HTML element. It's typically used to define a control that adjusts a variable, or a view that displays a variable, although more creative uses are possible.
If your HTML contains:
<span class="MyClass" data-var="cookies">
then when the tangle is created, MyClass will be instantiated for that element, and if there is an initialize
method, it will be called, and passed the name of the variable. If there is an update
method, it will be called when the specified variable is initialized or changed, and passed the value of the variable.
The initialize
method is a good place to add event listeners to the element, to set up an interactive control. The following example implements a control that increments the variable when it is clicked.
Tangle.classes.ClickableNumber = { initialize: function (element, options, tangle, variable) { element.onclick = function () { var value = tangle.getValue(variable); tangle.setValue(variable, value + 1); }; } };
The options
parameter is described below.
The update
method is for updating the element's appearance or contents when the variable changes. The following example resizes the element's width when the variable changes, perhaps for a bar in a bar chart.
Tangle.classes.StretchyBar = { update: function (element, value) { element.style.width = "" + value + "px"; } };
You may also define any methods that you want to use internally.
Tangle.classes.StretchyBar = { update: function (element, value) { var width = value * this.getStretchiness(); element.style.width = "" + width + "px"; }, getStretchiness: function () { ... } };
Because these are CSS classes as well as Tangle classes, you can often specify the element's style in a CSS file, and only deal with behavior in JavaScript.
.ClickableNumber { color: #46f; border-bottom: 1px dashed #46f; cursor: pointer; }
If Tangle.classes.MyClass
is a function instead of an object, Tangle will call it as a constructor, instead of directly invoking an initialize
method. So, if you're using a framework such as MooTools that has its own notion of what a "class" is, you can use its classes directly, and take advantage of inheritance and other features.
Tangle.classes.MyClass = new Class({ // assuming we're using MooTools Extends: MyBaseClass, initialize: function (element, options, tangle, variable) { this.parent(element, options, tangle, variable); ... } });
An HTML element can specify any number of variables, and they will all be passed to initialize
and update
. For example, if your class expects two variables:
<span class="MyClass" data-var="cookies cupcakes">
you would define it like so:
Tangle.classes.MyClass = { initialize: function (element, options, tangle, variable1, variable2) { ... }, update: function (element, value1, value2) { ... } };
This is useful for two-dimensional controls that adjust two variables at once, or views that display multiple variables.
The initialize
method is passed an options
parameter:
Tangle.classes.MyClass = { initialize: function (element, options, tangle, variable) { ... } };
The options
parameter is an object containing the data-
attributes of the elements. (The "data-
" prefix is removed.) If your HTML is:
<span class="MyClass" data-min="1" data-max="10">
then options
will be an object like so:
{ min:"1", max:"10" }
* * *
Tangle.formats.myFormat = function (value) { return "..."; }
A Tangle format defines how to present a value. If your HTML is:
<span data-var="fractionOfCookiesRemaining" data-format="percent">
then when the variable is updated, the following function will be called to turn the value into a string.
Tangle.formats.percent = function (value) { // formats 0.42 as "42%" return "" + Math.round(value * 100) + "%"; };
You can often use printf-style formats instead, but Tangle.formats
is available for your custom formatting needs. To use printf-style formats, you must include a sprintf library, such as the one that is provided with TangleKit.
If the data-var
attribute specifies multiple variables, all values will be passed to the function.
<span data-var="cookies cupcakes" data-format="myFormat">
Tangle.formats.myFormat = function (value1, value2) { return "..."; }
You can specify parameters in the data-format
attribute, separated by spaces. These strings will be passed to the function, after the variable values.
<span data-var="cookies" data-format="myFormat huge">
Tangle.formats.myFormat = function (value, size) { return "..."; }
* * *
TangleKit is an optional collection of Tangle classes and formats, for adjusting variables and visualizing values. You can grab whichever components you want, use them, extend them, modify them, or just learn from them and make your own.
TangleKit is currently pretty rudimentary, and not yet documented here. You can look through TangleKit.js for what's currently available. Perhaps you'd even like to contribute to it.